From 7d2cdba6f05ec4fd4cebf4ddc0ef1c741687a8c7 Mon Sep 17 00:00:00 2001 From: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Date: Wed, 8 Jul 2026 10:26:42 -0400 Subject: [PATCH] fix(desktop): defer event sync off setup path Finding: L2 event_sync boot reconcile did best-effort JSON reads, SQLite retention checks/writes, and event signing synchronously during Tauri setup after identity resolution. Snapshot the resolved owner keys, then spawn the reconcile after setup-critical boot work begins. The reconcile runs on spawn_blocking so no filesystem, SQLite, or signing work occupies the GUI setup path or an async worker. Secret-store identity resolution remains synchronous and out of scope by design. Co-authored-by: Tyler Longwell Signed-off-by: Tyler Longwell --- desktop/src-tauri/src/event_sync.rs | 18 ++++++++++++++++++ desktop/src-tauri/src/lib.rs | 11 ++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/desktop/src-tauri/src/event_sync.rs b/desktop/src-tauri/src/event_sync.rs index d8dfe94b1..efd16489a 100644 --- a/desktop/src-tauri/src/event_sync.rs +++ b/desktop/src-tauri/src/event_sync.rs @@ -19,6 +19,24 @@ pub fn run_event_sync(app: &tauri::AppHandle, owner_keys: &nostr::Keys) { crate::managed_agents::reconcile::reconcile_agents_to_events(app, owner_keys); } +/// Spawn the best-effort event reconcile off the synchronous Tauri setup path. +/// +/// The owner keys are cloned before spawning so the task never touches the +/// `AppState::keys` mutex. The reconcile itself is still synchronous JSON, +/// SQLite, and signing work, so it runs on the blocking pool rather than an +/// async worker. +pub fn spawn_event_sync(app: tauri::AppHandle, owner_keys: nostr::Keys) { + tauri::async_runtime::spawn(async move { + if let Err(e) = tauri::async_runtime::spawn_blocking(move || { + run_event_sync(&app, &owner_keys); + }) + .await + { + eprintln!("buzz-desktop: event-sync: spawn_blocking failed: {e}"); + } + }); +} + /// Reconcile `personas.json` into the persona-event retention store. /// /// Must run AFTER `migrate_packs_to_teams` (depends on field renames being diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 341c99d7b..def24183f 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -226,14 +226,13 @@ pub fn run() { resolve_persisted_identity(&app_handle, &state) .map_err(|e| -> Box { e.into() })?; - // Sync team-dir edits and reconcile persona/team events. Needs the - // resolved owner keys, so it runs after identity resolution. + // Snapshot owner keys after identity resolution; the best-effort + // event reconcile itself runs off the synchronous setup path below. let owner_keys = state .keys .lock() .map(|k| k.clone()) .map_err(|e| -> Box { e.to_string().into() })?; - event_sync::run_event_sync(&app_handle, &owner_keys); // Backfill the pinned persona snapshot for any pre-existing agent // that predates the record-authoritative-spawn cutover (persona_id @@ -332,6 +331,12 @@ pub fn run() { try_regenerate_nest(&app_handle); + // Sync team-dir edits and reconcile persona/team/agent events after + // setup can continue. It is best-effort retention backfill, unlike + // identity resolution above, so JSON/SQLite/signing work must not + // hold the boot path hostage. + event_sync::spawn_event_sync(app_handle.clone(), owner_keys); + if let Some(mgr) = huddle::models::global_model_manager() { mgr.start_stt_download(state.http_client.clone()); mgr.start_tts_download(state.http_client.clone());