#!/usr/bin/env bash # Prove the container can actually do the four things it exists for, before an # unattended agent spends an hour discovering otherwise. # # Every check here stands for a failure that has already happened once: a # display that was not there, a missing numpy that looked like a logic bug, a # /dev/shm too small for guest memory, a Vulkan stack with no ICD. set -u fail=0 ok() { printf ' \033[32m✔\033[0m %s\n' "$*"; } bad() { printf ' \033[31m✖\033[0m %s\n' "$*"; fail=$((fail+1)); } warn() { printf ' \033[33m!\033[0m %s\n' "$*"; } echo "── resources ──" # nproc shows the HOST's cores: --cpus is a quota, not a mask. Report both so # "12 cpus" is never mistaken for 12 cpus' worth of throughput. quota="unlimited" if [ -r /sys/fs/cgroup/cpu.max ]; then read -r q p < /sys/fs/cgroup/cpu.max [ "$q" != max ] && quota="$(( q / p )) (quota)" fi echo " cpus: $(nproc) visible, $quota" if [ -r /sys/fs/cgroup/memory.max ]; then m=$(cat /sys/fs/cgroup/memory.max) [ "$m" = max ] && warn "memory: UNLIMITED — the half-the-box cap is not applied" \ || ok "memory cap: $(( m / 1024 / 1024 / 1024 )) GiB" fi shm=$(df -BM /dev/shm | awk 'NR==2{print $2}' | tr -d M) # Guest memory for a 512 MB console plus the code cache does not fit in # Docker's 64 MB default, and the symptom is an mmap error, not a disk-full one. [ "${shm:-0}" -ge 512 ] && ok "/dev/shm: ${shm} MiB" || bad "/dev/shm only ${shm:-?} MiB — need >=512; pass --shm-size" echo "── toolchain ──" for t in clang clang++ cmake ninja cargo rustc python3 node claude; do command -v "$t" >/dev/null && ok "$t ($("$t" --version 2>/dev/null | head -1))" || bad "$t missing" done echo "── python (dynamic RE) ──" # numpy and PIL missing is the specific hole that silently disabled entities2.py # and every image oracle in the toolkit. for m in numpy PIL duckdb; do python3 -c "import $m" 2>/dev/null && ok "python: $m" || bad "python: $m MISSING" done echo "── display ──" if xdpyinfo >/dev/null 2>&1; then ok "display $DISPLAY ($(xdpyinfo | awk '/dimensions:/{print $2; exit}'))" pgrep -x openbox >/dev/null && ok "openbox running" || warn "no window manager — window geometry oracles will misread" out=$(screenshot /tmp/_doctor.png 2>&1) && [ -s /tmp/_doctor.png ] \ && ok "screenshot works -> $(identify -format '%wx%h' /tmp/_doctor.png 2>/dev/null || echo ok)" \ || bad "screenshot failed: $out" rm -f /tmp/_doctor.png else bad "no display on ${DISPLAY:-}" fi echo "── vulkan ──" if command -v vulkaninfo >/dev/null 2>&1; then dev=$(vulkaninfo --summary 2>/dev/null | grep -m3 -E 'deviceName' | sed 's/^ *//') [ -n "$dev" ] && { ok "Vulkan devices:"; echo "$dev" | sed 's/^/ /'; } \ || bad "vulkaninfo found no device (ICD missing?)" else bad "vulkaninfo missing" fi # Judge by what enumerated, not by whether a device node is present: an NVIDIA # card needs the NVIDIA Container Toolkit, and /dev/dri alone does nothing. case "${dev:-}" in *llvmpipe*|*lavapipe*) warn "SOFTWARE Vulkan only — correct but slow." command -v nvidia-smi >/dev/null 2>&1 \ && warn " host has an NVIDIA GPU: install nvidia-container-toolkit for hardware" ;; "") ;; *) ok "hardware Vulkan" ;; esac echo "── project ──" [ -d /work/xenia-canary ] && ok "/work/xenia-canary" || bad "/work/xenia-canary not mounted" [ -d /work/Syplheed-Reborn ] && ok "/work/Syplheed-Reborn" || bad "/work/Syplheed-Reborn not mounted" iso=$(find /work -maxdepth 2 -type f -iname '*.iso' -printf '%s\t%p\n' 2>/dev/null | sort -rn | head -1 | cut -f2-) [ -n "$iso" ] && ok "ISO: $iso" || warn "no ISO under /work — run-canary needs SYLPH_ISO" [ -d /work/sylph_extract/dat ] && ok "extracted disc (disc-gated tests will run)" \ || warn "no extracted disc — Reborn disc tests will SKIP" [ -w /sylph-home/re/.claude ] && ok "~/.claude writable (token refresh works)" \ || warn "~/.claude not writable — Claude Code may fail to refresh auth" echo [ "$fail" -eq 0 ] && { echo "all good."; exit 0; } echo "$fail check(s) failed."; exit 1