diff --git a/desktop/scripts/fix-appimage.sh b/desktop/scripts/fix-appimage.sh
index 45f537bb72..973d9fbb62 100755
--- a/desktop/scripts/fix-appimage.sh
+++ b/desktop/scripts/fix-appimage.sh
@@ -114,6 +114,84 @@ echo "==> Symlinking system GStreamer plugin directory"
 rm -rf "$LIBDIR/gstreamer-1.0"
 ln -s "/usr/lib/$MULTIARCH/gstreamer-1.0" "$LIBDIR/gstreamer-1.0"
 
+# WebKitGTK 2.52 Skia's GPU path goes through Vulkan/radv. On non-conformant
+# AMD RDNA4 (gfx1200) that path paints nothing — transparent ghost window —
+# while WEBKIT_DISABLE_DMABUF_RENDERER does not help (#2643). Prefer Skia CPU
+# raster for AppImage launches; operators can override with =0.
+echo "==> Preferring WebKitGTK Skia CPU rendering for AppImage launches"
+HOOK_DIR="$WORKDIR/squashfs-root/apprun-hooks"
+mkdir -p "$HOOK_DIR"
+cat > "$HOOK_DIR/99-buzz-webkit-skia-cpu.sh" <<'EOF'
+# Allow operators to override (e.g. WEBKIT_SKIA_ENABLE_CPU_RENDERING=0) if needed.
+export WEBKIT_SKIA_ENABLE_CPU_RENDERING="${WEBKIT_SKIA_ENABLE_CPU_RENDERING:-1}"
+EOF
+if [[ -f "$WORKDIR/squashfs-root/AppRun" ]] \
+  && ! grep -q 'WEBKIT_SKIA_ENABLE_CPU_RENDERING' "$WORKDIR/squashfs-root/AppRun"; then
+  if ! grep -q 'apprun-hooks' "$WORKDIR/squashfs-root/AppRun"; then
+    tmp_apprun="$(mktemp)"
+    {
+      if head -n1 "$WORKDIR/squashfs-root/AppRun" | grep -q '^#!'; then
+        head -n1 "$WORKDIR/squashfs-root/AppRun"
+        echo 'export WEBKIT_SKIA_ENABLE_CPU_RENDERING="${WEBKIT_SKIA_ENABLE_CPU_RENDERING:-1}"'
+        tail -n +2 "$WORKDIR/squashfs-root/AppRun"
+      else
+        echo 'export WEBKIT_SKIA_ENABLE_CPU_RENDERING="${WEBKIT_SKIA_ENABLE_CPU_RENDERING:-1}"'
+        cat "$WORKDIR/squashfs-root/AppRun"
+      fi
+    } > "$tmp_apprun"
+    mv "$tmp_apprun" "$WORKDIR/squashfs-root/AppRun"
+    chmod +x "$WORKDIR/squashfs-root/AppRun"
+  fi
+fi
+
+# Fedora's COLRv1 Noto Color Emoji trips an assertion in WebKitGTK/Skia and
+# aborts the AppImage after a blank window (#2548). Reject that family so
+# emoji fall back to a non-COLRv1 face (or to monochrome glyphs).
+echo "==> Rejecting COLRv1 system color-emoji fonts for AppImage launches"
+FC_DIR="$WORKDIR/squashfs-root/usr/etc/fonts"
+mkdir -p "$FC_DIR"
+cat > "$FC_DIR/fonts.conf" <<'EOF'
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
+<fontconfig>
+  <!-- Keep the host fontconfig, then drop COLRv1 color-emoji faces that crash
+       bundled WebKitGTK/Skia on Fedora (#2548). -->
+  <include ignore_missing="yes">/etc/fonts/fonts.conf</include>
+  <selectfont>
+    <rejectfont>
+      <pattern>
+        <patelt name="family"><string>Noto Color Emoji</string></patelt>
+      </pattern>
+    </rejectfont>
+  </selectfont>
+</fontconfig>
+EOF
+cat > "$HOOK_DIR/98-buzz-fontconfig-no-colrv1.sh" <<'EOF'
+# Prefer the AppImage fontconfig that rejects COLRv1 Noto Color Emoji (#2548).
+# Operators can point FONTCONFIG_FILE elsewhere to override.
+if [ -z "${FONTCONFIG_FILE:-}" ] && [ -n "${APPDIR:-}" ] && [ -f "$APPDIR/usr/etc/fonts/fonts.conf" ]; then
+  export FONTCONFIG_FILE="$APPDIR/usr/etc/fonts/fonts.conf"
+fi
+EOF
+if [[ -f "$WORKDIR/squashfs-root/AppRun" ]] \
+  && ! grep -q 'buzz-fontconfig-no-colrv1\|FONTCONFIG_FILE=.*usr/etc/fonts' "$WORKDIR/squashfs-root/AppRun"; then
+  if ! grep -q 'apprun-hooks' "$WORKDIR/squashfs-root/AppRun"; then
+    tmp_apprun="$(mktemp)"
+    {
+      if head -n1 "$WORKDIR/squashfs-root/AppRun" | grep -q '^#!'; then
+        head -n1 "$WORKDIR/squashfs-root/AppRun"
+        echo 'if [ -z "${FONTCONFIG_FILE:-}" ] && [ -n "${APPDIR:-}" ] && [ -f "$APPDIR/usr/etc/fonts/fonts.conf" ]; then export FONTCONFIG_FILE="$APPDIR/usr/etc/fonts/fonts.conf"; fi'
+        tail -n +2 "$WORKDIR/squashfs-root/AppRun"
+      else
+        echo 'if [ -z "${FONTCONFIG_FILE:-}" ] && [ -n "${APPDIR:-}" ] && [ -f "$APPDIR/usr/etc/fonts/fonts.conf" ]; then export FONTCONFIG_FILE="$APPDIR/usr/etc/fonts/fonts.conf"; fi'
+        cat "$WORKDIR/squashfs-root/AppRun"
+      fi
+    } > "$tmp_apprun"
+    mv "$tmp_apprun" "$WORKDIR/squashfs-root/AppRun"
+    chmod +x "$WORKDIR/squashfs-root/AppRun"
+  fi
+fi
+
 echo "==> Repacking AppImage"
 # Pass a pinned type2 runtime when provided (CI sets APPIMAGETOOL_RUNTIME_FILE);
 # without it appimagetool downloads the runtime from its mutable `continuous`
diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs
index 3688ee3a62..b09b00e88b 100644
--- a/desktop/src-tauri/src/lib.rs
+++ b/desktop/src-tauri/src/lib.rs
@@ -135,6 +135,14 @@ async fn wait_for_stable_initial_window_geometry<R: tauri::Runtime>(window: &tau
 
 #[cfg_attr(mobile, tauri::mobile_entry_point)]
 pub fn run() {
+    // WebKitGTK 2.52 Skia GPU/Vulkan paints nothing on non-conformant AMD RDNA4
+    // (radv gfx1200): transparent ghost window (#2643). Prefer CPU raster when
+    // unset; operators can force GPU with WEBKIT_SKIA_ENABLE_CPU_RENDERING=0.
+    #[cfg(target_os = "linux")]
+    if std::env::var_os("WEBKIT_SKIA_ENABLE_CPU_RENDERING").is_none() {
+        std::env::set_var("WEBKIT_SKIA_ENABLE_CPU_RENDERING", "1");
+    }
+
     // mesh-llm's async chains (model download, node start/join) overflow
     // tokio's default 2 MiB worker stacks — a stack-guard SIGABRT, not a
     // panic. Upstream mesh-llm and mesh-console both run on 8 MiB worker
diff --git a/desktop/src-tauri/src/managed_agents/git_bash.rs b/desktop/src-tauri/src/managed_agents/git_bash.rs
index dab9e887c8..fdfc4e60e7 100644
--- a/desktop/src-tauri/src/managed_agents/git_bash.rs
+++ b/desktop/src-tauri/src/managed_agents/git_bash.rs
@@ -302,7 +302,9 @@ fn scan_path_for_command(
 ) -> Option<PathBuf> {
     let needs_exe = name.extension().is_none();
     std::env::split_paths(path_env).find_map(|dir| {
-        if system_root.is_some_and(|root| is_under_dir(&dir, root)) {
+        // System32 bash and %LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe are
+        // WSL app-execution aliases, not Git Bash (#2685).
+        if system_root.is_some_and(|root| is_under_dir(&dir, root)) || is_windows_apps_dir(&dir) {
             return None;
         }
         let candidate = dir.join(name);
@@ -320,6 +322,19 @@ fn scan_path_for_command(
     })
 }
 
+/// Windows exposes `bash.exe` app-execution aliases under WindowsApps that
+/// launch WSL rather than Git Bash. Skip those PATH entries so discovery does
+/// not boot `wsl.exe` on every unresolved command probe.
+#[cfg(windows)]
+fn is_windows_apps_dir(dir: &Path) -> bool {
+    dir.components().any(|component| {
+        component
+            .as_os_str()
+            .to_string_lossy()
+            .eq_ignore_ascii_case("WindowsApps")
+    })
+}
+
 #[cfg(windows)]
 fn is_under_dir(dir: &Path, root: &Path) -> bool {
     let mut dir_components = dir.components();
@@ -501,6 +516,37 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_windows_apps_bash_alias_is_not_treated_as_git_bash() {
+        let temp = tempdir().expect("tempdir");
+        let windows_apps = temp.path().join("Microsoft").join("WindowsApps");
+        let alias = windows_apps.join("bash.exe");
+        let git = temp.path().join("Git").join("cmd").join("git.exe");
+        let bash = temp.path().join("Git").join("bin").join("bash.exe");
+        std::fs::create_dir_all(&windows_apps).expect("mkdir WindowsApps");
+        std::fs::create_dir_all(git.parent().expect("git parent")).expect("mkdir git");
+        std::fs::create_dir_all(bash.parent().expect("bash parent")).expect("mkdir bash");
+        std::fs::write(alias, []).expect("alias");
+        std::fs::write(&git, []).expect("git");
+        std::fs::write(&bash, []).expect("bash");
+
+        let path =
+            std::env::join_paths([windows_apps, git.parent().expect("cmd dir").to_path_buf()])
+                .expect("PATH");
+        assert_eq!(
+            resolve_git_bash_no_registry(
+                path.to_str().expect("utf8"),
+                None,
+                None,
+                None,
+                None,
+                None,
+                None,
+            ),
+            Some(bash)
+        );
+    }
+
     #[test]
     fn test_effective_buzz_shell_override_marks_agent_ready() {
         let temp = tempdir().expect("tempdir");
