Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: harden emacs backend restart flow

+97 -31
+91 -31
dotfiles/dot_config/emacs.el
··· 1147 1147 cmd))) 1148 1148 (ac-debug-log (format "Running command: ac-%s" actual-cmd)) 1149 1149 ;; Create eat buffer directly without blocking 1150 - (let ((buf (generate-new-buffer buf-name))) 1151 - (with-current-buffer buf 1152 - (eat-mode) 1153 - (eat-exec buf buf-name "/usr/bin/fish" nil 1154 - (list "-c" (format "ac-%s" actual-cmd)))) 1155 - (switch-to-buffer buf) 1156 - ;; Yield to let Emacs process events (prevents freeze) 1157 - (redisplay t))) 1150 + (condition-case err 1151 + (let ((buf (generate-new-buffer buf-name))) 1152 + (with-current-buffer buf 1153 + (eat-mode) 1154 + (eat-exec buf buf-name "/usr/bin/fish" nil 1155 + (list "-c" (format "ac-%s" actual-cmd)))) 1156 + (switch-to-buffer buf) 1157 + ;; Yield to let Emacs process events (prevents freeze) 1158 + (redisplay t)) 1159 + (error 1160 + (ac-debug-log (format "Error starting ac-%s in tab %s: %s" actual-cmd tab-name err))))) 1158 1161 1159 1162 (setq cmd-index (1+ cmd-index))) 1160 1163 ··· 1164 1167 1165 1168 (defvar ac--startup-lock-file "/tmp/emacs-backend-startup.lock" 1166 1169 "Lock file indicating aesthetic-backend is starting (crash monitor will be gentle).") 1170 + 1171 + (defvar ac--backend-initializing nil 1172 + "Whether aesthetic-backend is currently initializing.") 1173 + 1174 + (defvar ac--backend-complete nil 1175 + "Whether aesthetic-backend completed initialization.") 1176 + 1177 + (defvar ac--backend-retry-count 0 1178 + "Number of aesthetic-backend retries attempted this session.") 1179 + 1180 + (defvar ac--backend-max-retries 1 1181 + "Maximum number of aesthetic-backend retries.") 1167 1182 1168 1183 (cl-defun aesthetic-backend (target-tab) 1169 1184 (interactive) 1170 1185 (setq target-tab (or target-tab "artery")) 1171 1186 1172 1187 ;; Prevent double-invocation race condition 1173 - (when ac--backend-started 1174 - (ac-debug-log (format "aesthetic-backend already started, just switching to tab: %s" target-tab)) 1188 + (when (and ac--backend-started ac--backend-complete) 1189 + (ac-debug-log (format "aesthetic-backend already complete, switching to tab: %s" target-tab)) 1175 1190 ;; Just switch to the requested tab if backend already running 1176 1191 (run-with-timer 0.5 nil 1177 1192 (lambda (target) ··· 1181 1196 (error nil))) 1182 1197 target-tab) 1183 1198 (cl-return-from aesthetic-backend nil)) 1184 - (setq ac--backend-started t) 1199 + (when ac--backend-initializing 1200 + (ac-debug-log "aesthetic-backend already initializing; skipping duplicate start") 1201 + (cl-return-from aesthetic-backend nil)) 1202 + (setq ac--backend-started t 1203 + ac--backend-initializing t 1204 + ac--backend-complete nil) 1185 1205 1186 1206 ;; Create startup lock file to tell crash monitor we're starting up 1187 1207 ;; This prevents false "unresponsive" detection during heavy init ··· 1221 1241 (kill-buffer buf))) 1222 1242 (error nil)) 1223 1243 1224 - ;; Helper to check if a tab with a given name exists 1225 - (defun ac--tab-exists-p (name) 1226 - "Return t if a tab named NAME exists." 1227 - (seq-find (lambda (tab) (string= name (alist-get 'name tab))) 1228 - (tab-bar-tabs))) 1229 - 1230 1244 ;; Initialize the first tab as "artery" with artery CLI (dev mode with hot-reload) 1231 1245 ;; Only create if the tab doesn't already exist 1232 1246 (unless (ac--tab-exists-p "artery") ··· 1235 1249 (buf (or (get-buffer "🩸-artery") 1236 1250 (generate-new-buffer "🩸-artery")))) 1237 1251 (with-current-buffer buf 1238 - (unless (eq major-mode 'eat-mode) 1239 - (eat-mode) 1240 - (eat-exec buf "🩸-artery" "/usr/bin/fish" nil '("-c" "ac-artery-dev"))) 1241 - ;; Enable semi-char mode so TUI gets individual keypresses 1242 - (eat-semi-char-mode) 1243 - ;; Disable evil mode for artery - use emacs state 1244 - (when (and (boundp 'evil-mode) evil-mode) 1245 - (evil-emacs-state))) 1252 + (condition-case err 1253 + (progn 1254 + (unless (eq major-mode 'eat-mode) 1255 + (eat-mode) 1256 + (eat-exec buf "🩸-artery" "/usr/bin/fish" nil '("-c" "ac-artery-dev"))) 1257 + ;; Enable semi-char mode so TUI gets individual keypresses 1258 + (eat-semi-char-mode) 1259 + ;; Disable evil mode for artery - use emacs state 1260 + (when (and (boundp 'evil-mode) evil-mode) 1261 + (evil-emacs-state))) 1262 + (error 1263 + (ac-debug-log (format "Error starting artery eat buffer: %s" err))))) 1246 1264 (switch-to-buffer buf) 1247 1265 (redisplay t))) ; Yield to prevent freeze 1248 1266 ··· 1255 1273 (buf (or (get-buffer "🐟-fishy") 1256 1274 (generate-new-buffer "🐟-fishy")))) 1257 1275 (with-current-buffer buf 1258 - (unless (eq major-mode 'eat-mode) 1259 - (eat-mode) 1260 - (eat-exec buf "🐟-fishy" "/usr/bin/fish" nil nil)) 1261 - (eat-semi-char-mode) 1262 - (when (and (boundp 'evil-mode) evil-mode) 1263 - (evil-emacs-state))) 1276 + (condition-case err 1277 + (progn 1278 + (unless (eq major-mode 'eat-mode) 1279 + (eat-mode) 1280 + (eat-exec buf "🐟-fishy" "/usr/bin/fish" nil nil)) 1281 + (eat-semi-char-mode) 1282 + (when (and (boundp 'evil-mode) evil-mode) 1283 + (evil-emacs-state))) 1284 + (error 1285 + (ac-debug-log (format "Error starting fishy eat buffer: %s" err))))) 1264 1286 (switch-to-buffer buf) 1265 1287 (redisplay t))) 1266 1288 ··· 1304 1326 1305 1327 ;; Start CDP tunnel in background after tabs are created 1306 1328 (run-with-timer 8.0 nil #'ac-start-cdp-tunnel-async) 1329 + 1330 + ;; Retry if tabs did not initialize properly 1331 + (run-with-timer 9.0 nil 1332 + (lambda (target) 1333 + (when (and (not ac--backend-complete) 1334 + (< ac--backend-retry-count ac--backend-max-retries)) 1335 + (setq ac--backend-retry-count (1+ ac--backend-retry-count)) 1336 + (ac-debug-log (format "Retrying aesthetic-backend (attempt %d)" ac--backend-retry-count)) 1337 + (aesthetic-backend target))) 1338 + target-tab) 1307 1339 1308 1340 ;; Remove startup lock file to signal we're done initializing 1309 1341 ;; Give a bit more time for everything to settle ··· 1312 1344 (when (file-exists-p ac--startup-lock-file) 1313 1345 (delete-file ac--startup-lock-file) 1314 1346 (ac-debug-log "Removed startup lock - crash monitor now active") 1347 + (setq ac--backend-complete t 1348 + ac--backend-initializing nil) 1315 1349 (ac-perf-log "aesthetic-backend initialization complete"))))) 1316 1350 1317 1351 (defun ac-start-cdp-tunnel-async () ··· 1335 1369 (defvar ac--backend-started nil 1336 1370 "Flag to ensure aesthetic-backend only runs once.") 1337 1371 1372 + (defun ac--backend-tabs-minimal-p () 1373 + "Return t if minimal tabs are present (artery + fishy)." 1374 + (and (ac--tab-exists-p "artery") 1375 + (ac--tab-exists-p "fishy"))) 1376 + 1377 + (defun ac--ensure-backend (&optional reason) 1378 + "Ensure aesthetic-backend is running; retries if needed. 1379 + REASON is logged for debugging." 1380 + (when (and (not ac--backend-initializing) 1381 + (not (ac--backend-tabs-minimal-p)) 1382 + (< ac--backend-retry-count ac--backend-max-retries)) 1383 + (setq ac--backend-retry-count (1+ ac--backend-retry-count)) 1384 + (ac-debug-log (format "Ensuring aesthetic-backend (attempt %d) reason=%s" 1385 + ac--backend-retry-count (or reason "unknown"))) 1386 + (aesthetic-backend "artery"))) 1387 + 1338 1388 (defun ac--maybe-start-backend (frame) 1339 1389 "Start aesthetic-backend when first terminal frame connects." 1340 1390 (when (and (not ac--backend-started) ··· 1348 1398 (condition-case err 1349 1399 (aesthetic-backend "artery") 1350 1400 (error (message "Error starting aesthetic-backend: %s" err))))))) 1401 + 1402 + ;; Self-heal: if backend didn't start after daemon restart, try again once. 1403 + (add-hook 'emacs-startup-hook 1404 + (lambda () 1405 + (run-with-timer 6 nil 1406 + (lambda () 1407 + (when (and (not ac--backend-started) 1408 + (frame-live-p (selected-frame)) 1409 + (<= (length (tab-bar-tabs)) 1)) 1410 + (ac--ensure-backend "startup self-heal")))))) 1351 1411 1352 1412 ;; Auto-start full backend on first terminal frame 1353 1413 (add-hook 'after-make-frame-functions #'ac--maybe-start-backend)
+6
monitor-emacs.sh
··· 68 68 # Verify 69 69 if timeout 5 emacsclient -e "t" >/dev/null 2>&1; then 70 70 log "✅ WATCHDOG: Emacs daemon restarted successfully" 71 + # Trigger aesthetic-backend so tabs/terminals come back after crash 72 + if timeout 15 emacsclient -e "(aesthetic-backend \"artery\")" >/dev/null 2>&1; then 73 + log "🧭 WATCHDOG: aesthetic-backend triggered after restart" 74 + else 75 + log "⚠️ WATCHDOG: Failed to trigger aesthetic-backend after restart" 76 + fi 71 77 show_warning "$reason" 72 78 return 0 73 79 else