chore: hand the workspace over — the gated launchers, and the state of play

Resuming happens on the other machine, so anything that lived only in this
workspace either comes into the repo or gets named as something to carry.

**The launchers come in.** `run-canary-safe.sh` (Wine) and
`run-canary-native-safe.sh` (native) are the ONLY sanctioned way to start Canary
from the editor — they force software Vulkan and refuse to launch while a
hardware Vulkan device is visible, because Canary on the AMD GPU takes VS Code
with it. They sat in the workspace root, outside git, hardcoding one machine's
absolute paths, which is exactly why they could not be checked in. They now
derive the workspace from their own location and honour `$SYLPHEED_ISO` and
`$CANARY_BIN`, so the layout is a default rather than a requirement. Same for
`run-canary-native.sh` (interactive, hardware Vulkan — not from the IDE),
`diagnose-freeze.sh`, `live-guest-state.sh`, `heaptrack-wrap.sh` and the
`asound-null.conf` the native launcher needs beside it.

Both safe launchers were run from `tools/` before this commit: the native one
reached content in 20 s (`VERDICT: HEALTHY`, title + 25,398 XMA lines, no
faults), the Wine one ran 25 s and logged 4 `ADV.wmv` hits.

**`docs/agents/HANDOFF-2026-09-18.md`** records what changed since 2026-09-16,
what is on the server, what cannot travel through git, and the traps this machine
paid for — chiefly that the guest-PC branch probe exists only on the fork's
snapshot branch, so a rebuild from `sylpheed-re` silently loses it.

**`HANDOFF-2026-09-06.md`** gets a correction note rather than an edit: a plain
clone works again now that the 545 MB branch is gone, and its baselines are two
baselines old.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
sim
2026-09-18 07:17:36 +02:00
parent 4efd9f5936
commit e45a56ad83
9 changed files with 580 additions and 0 deletions

98
tools/run-canary-native-safe.sh Executable file
View File

@@ -0,0 +1,98 @@
#!/usr/bin/env bash
# SAFE launcher for the NATIVE Linux Xenia-Canary build on THIS box.
#
# Same safety contract as run-canary-safe.sh (the Wine/Windows variant): the AMD
# GPU + GPU-accelerated VS Code means a hardware-Vulkan render CRASHES VS Code.
# This wrapper forces software Vulkan (lavapipe/llvmpipe) IN THE SAME process and
# HARD-REFUSES to launch if any hardware Vulkan device is still visible.
#
# Difference vs run-canary-safe.sh: runs the native ELF directly (NO wine).
#
# Usage: tools/run-canary-native-safe.sh [seconds]
# env: CANARY_BIN override binary (default = worktree Release build)
# CANARY_EXTRA_ARGS space-separated extra cvars (values w/o spaces)
# Output: /tmp/canary_native.stdout (+ xenia.log in the binary dir). Prints rc + ADV.wmv hits.
set -u
# --- force software Vulkan, belt-and-suspenders across loader/driver variants ---
export VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.json
export VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json
export MESA_VK_DEVICE_SELECT=llvmpipe
export LIBGL_ALWAYS_SOFTWARE=1
# --- keep background runs SILENT *without* killing the audio system ---
# DO NOT use --apu=nop: with no audio system the guest's XAudio render client is
# NULL and Project Sylpheed derefs null+0x3C at PC 0x824D7C40 -> a FALSE boot
# crash that never happens in interactive runs. Instead keep the ALSA apu ALIVE
# and route PCM "default" to the null device (see asound-null.conf): silent, but
# snd_pcm_open succeeds so the guest is happy. --mute does NOT silence ALSA.
export SDL_AUDIODRIVER=dummy
export ALSA_CONFIG_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/asound-null.conf"
[ -f "$ALSA_CONFIG_PATH" ] || { echo "ABORT: asound-null.conf missing next to script"; exit 4; }
# Paths derive from where this script sits — see the note in run-canary-safe.sh.
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE="$(cd "$HERE/../.." && pwd)"
BIN_DEFAULT="$WORKSPACE/xenia-canary-native/build/bin/Linux/Release/xenia_canary"
BIN_EXE="${CANARY_BIN:-$BIN_DEFAULT}"
ISO="${SYLPHEED_ISO:-$WORKSPACE/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso}"
[ -f "$ISO" ] || { echo "ABORT: no ISO at '$ISO' — set \$SYLPHEED_ISO"; exit 4; }
SECS="${1:-95}"
[ -x "$BIN_EXE" ] || { echo "ABORT: native binary not found/executable: $BIN_EXE"; exit 4; }
# --- HARD PRE-FLIGHT GATE: only proceed if Vulkan exposes software devices ONLY ---
devs="$(vulkaninfo --summary 2>/dev/null | grep -i deviceName || true)"
if echo "$devs" | grep -qiE 'radv|amd|radeon|nvidia|geforce|intel\b'; then
echo "ABORT(pre-flight): a HARDWARE Vulkan device is still visible -> refusing (would crash VS Code):"
echo "$devs"
exit 3
fi
if ! echo "$devs" | grep -qiE 'llvmpipe'; then
echo "ABORT(pre-flight): llvmpipe not visible; lavapipe ICD missing? devs=[$devs]"
exit 3
fi
echo "pre-flight OK: software-only Vulkan -> $devs"
# --- clean slate ---
pkill -x xenia_canary 2>/dev/null; pkill -x Xvfb 2>/dev/null; sleep 1
Xvfb :99 -screen 0 1280x720x24 -nolisten tcp >/tmp/xvfb.log 2>&1 &
XVFB=$!
sleep 2
export DISPLAY=:99
cd "$(dirname "$BIN_EXE")" || { echo "ABORT: bin dir missing"; kill "$XVFB" 2>/dev/null; exit 4; }
rm -f xenia.log
# Real ALSA apu (routed to null sink via ALSA_CONFIG_PATH) so the guest gets a
# valid render client; --mute belt-and-suspenders. NEVER add --apu=nop here.
args=(--log_level=3 --mute=true)
if [ -n "${CANARY_EXTRA_ARGS:-}" ]; then
read -ra EXTRA <<< "$CANARY_EXTRA_ARGS"
args+=("${EXTRA[@]}")
fi
echo "launch (native, sw-vulkan, SILENT null-ALSA, ${SECS}s): $(basename "$BIN_EXE") ${args[*]}"
# -k 5: xenia catches SIGTERM and can hang on shutdown; force-KILL 5s later.
timeout -k 5 "$SECS" "$BIN_EXE" "$ISO" "${args[@]}" >/tmp/canary_native.stdout 2>&1
rc=$?
pkill -x xenia_canary 2>/dev/null; kill "$XVFB" 2>/dev/null; pkill -x Xvfb 2>/dev/null
# --- trustworthy boot-health verdict (NOT log-line-count, NOT rc) ---
# A healthy boot reaches actual content: title loaded + XMA audio decoding + NO
# host-exception fault loop. rc=137 is EXPECTED (timeout -k kill), not a failure.
faults=$(grep -ac 'Access Violation' /tmp/canary_native.stdout 2>/dev/null)
gthrow=$(grep -ac 'GUEST-THROW' xenia.log 2>/dev/null)
title=$(grep -ac 'Title name: PROJECT SYLPHEED' xenia.log 2>/dev/null)
xma=$(grep -acE 'XmaContext|Processing context' xenia.log 2>/dev/null)
echo "rc=$rc logsize=$(wc -c < xenia.log 2>/dev/null)"
echo "boot-health: title=$title xma=$xma faults=$faults guest_throw=$gthrow"
if [ "$title" -ge 1 ] && [ "$xma" -ge 100 ] && [ "$faults" -eq 0 ] && [ "$gthrow" -eq 0 ]; then
echo "VERDICT: HEALTHY (reached content, no crash)"
elif [ "$gthrow" -ge 1 ]; then
echo "VERDICT: GUEST-THROW CRASH (the real bug) -- see GUEST-THROW lines in xenia.log"
elif [ "$faults" -ge 1 ]; then
echo "VERDICT: FAULT (host access-violation loop) -- crash PC:"
grep -aoE 'PC: 0x[0-9A-Fa-f]+' /tmp/canary_native.stdout | sort | uniq -c | head -3
else
echo "VERDICT: INCOMPLETE (did not reach content in ${SECS}s -- raise timeout?)"
fi