@@ -1116,10 +1116,11 @@ pub fn run() {
11161116 // macOS keeps for the previous binary's code signature.
11171117 // Without this, System Settings shows the toggle on but
11181118 // the new binary cannot actually use the permission.
1119- if updater:: tcc_reset:: should_reset_for_upgrade (
1119+ let did_upgrade = updater:: tcc_reset:: should_reset_for_upgrade (
11201120 sidecar. last_launched_version . as_deref ( ) ,
11211121 & running_version,
1122- ) {
1122+ ) ;
1123+ if did_upgrade {
11231124 updater:: tcc_reset:: tccutil_reset ( & app. config ( ) . identifier ) ;
11241125 }
11251126
@@ -1135,16 +1136,41 @@ pub fn run() {
11351136 updater_state
11361137 . set_last_seen_update_version ( sidecar. last_seen_update_version . clone ( ) ) ;
11371138
1138- // Record the running version so the next launch can
1139- // detect another upgrade. Best-effort; failure to write
1140- // the sidecar is logged inside SnoozeSidecar::save.
1139+ // Record the running version BEFORE any potential restart
1140+ // so the post-restart launch reads a sidecar where the
1141+ // recorded version matches the running version. Without
1142+ // this, the next launch would see another "upgrade" and
1143+ // restart-loop forever.
11411144 sidecar. last_launched_version = Some ( running_version) ;
11421145 if let Some ( path) = sidecar_path. as_ref ( ) {
11431146 if let Err ( e) = sidecar. save ( path) {
11441147 eprintln ! ( "thuki: [updater] failed to persist sidecar: {e}" ) ;
11451148 }
11461149 }
11471150
1151+ // After `tccutil reset` clears the TCC.db entry for Thuki,
1152+ // the running process retains stale per-PID tracking inside
1153+ // macOS's `tccd` daemon. Subsequent `AXIsProcessTrusted`
1154+ // calls from THIS process do not register the new csreq, so
1155+ // Thuki is missing from System Settings → Privacy &
1156+ // Security → Accessibility and the user has no in-app path
1157+ // to grant. Empirically (user-reproduced) the only fix is
1158+ // a fresh process: `tccd` sees a brand new PID and
1159+ // registers it normally on the first AX call from
1160+ // onboarding. The restart is deferred so Tauri finishes
1161+ // wiring up the rest of `setup` before we tear it down.
1162+ if did_upgrade {
1163+ let app_handle = app. handle ( ) . clone ( ) ;
1164+ tauri:: async_runtime:: spawn ( async move {
1165+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 150 ) ) . await ;
1166+ eprintln ! (
1167+ "thuki: [updater] relaunching after TCC reset \
1168+ to refresh tccd PID tracking"
1169+ ) ;
1170+ app_handle. restart ( ) ;
1171+ } ) ;
1172+ }
1173+
11481174 let ( interval, auto_check) = {
11491175 let cfg = app. state :: < parking_lot:: RwLock < crate :: config:: AppConfig > > ( ) ;
11501176 let g = cfg. read ( ) ;
0 commit comments