diff --git a/doc/README.md b/doc/README.md
index cc1cb71..8ef478b 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -38,21 +38,23 @@ line-by-line code — so they stay true as the implementation evolves.
Prereqs on this box (already installed): JDK 17, Go, Android SDK at
`~/android-sdk` (platform-tools, build-tools 35.0.0, emulator, NDK),
-`gogio@v0.10.0` in `~/go/bin`, `apktool` at `/tmp/apktool.jar`, debug
-keystore at `~/.android/debug.keystore`. Env vars are set in `~/.bashrc`.
+`gogio@v0.10.0` in `~/go/bin`, debug keystore at
+`~/.android/debug.keystore`. Env vars are set in `~/.bashrc`.
-The full recipe is `/tmp/build_pad.sh` (machine-local). Steps:
+**Build + install with `./scripts/build_emu.sh`** (add `--no-install` to
+build only). It is self-contained: checks the prereqs, auto-downloads
+apktool v3.0.3 to `~/android-sdk/tools/apktool.jar` if missing, and uses a
+temporary work dir. Output: `cmd/pad/pad-emu.apk`.
-1. `gogio -target android -targetsdk 35 -arch amd64 -o /tmp/pad-raw.apk .`
- (from `cmd/pad/`)
-2. `apktool d /tmp/pad-raw.apk -o /tmp/pad_decoded -f`
+What it does, and why (gogio cannot inject manifest permissions):
+
+1. `gogio -target android -targetsdk 35 -arch amd64` → raw APK (from `cmd/pad/`)
+2. `apktool d` → decode
3. Inject ``
- into the decoded `AndroidManifest.xml` (Android 15 needs it for
- `/storage/emulated/0`).
-4. `apktool b /tmp/pad_decoded -o /tmp/pad-unsigned.apk`
-5. `apksigner sign --ks ~/.android/debug.keystore --ks-pass pass:android
- --key-pass pass:android --ks-key-alias androiddebugkey --out cmd/pad/pad-emu.apk /tmp/pad-unsigned.apk`
-6. `adb install -r cmd/pad/pad-emu.apk`
+ (without it, Android 11+ blocks listing `/storage/emulated/0`)
+4. `apktool b` → rebuild
+5. `apksigner sign` with the debug key → `cmd/pad/pad-emu.apk`
+6. `adb install -r` (skipped with `--no-install`)
Run the emulator (headless, AVD `pad_avd`, API 35):
diff --git a/doc/development_plan.md b/doc/development_plan.md
index 398317f..df26076 100644
--- a/doc/development_plan.md
+++ b/doc/development_plan.md
@@ -153,7 +153,7 @@ API 35 `google_apis/x86_64` system image installed to `~/android-sdk`; AVD
`pad_avd` (Pixel 6 profile) boots with KVM (`/dev/kvm` chmod 666). Build recipe:
`gogio -target android -targetsdk 35 -arch amd64` → inject
`MANAGE_EXTERNAL_STORAGE` via apktool → sign with the debug keystore →
-`adb install -r` (scripted in `/tmp/build_pad.sh`).
+`adb install -r` (scripted in `scripts/build_emu.sh`).
**On-device IME validation — PASSING** (Gboard, API 35; observed via the gated
`imeDebugLog` trace + logcat + autosaved file diff + screenshots):
@@ -248,8 +248,8 @@ but are not needed for a usable v1.
rules; over-detailed companion docs deleted — see `doc/README.md` policy).
- ✓ Amend `spec.md` per §7 (2026-08-16: rewritten; unbuilt features moved to an
explicit “deferred” table; see the corrections noted under §7 below).
-- ✓ Build/install recipe (2026-08-16: in `doc/README.md`; machine-local script
- `/tmp/build_pad.sh`).
+- ✓ Build/install recipe (2026-08-16: in `doc/README.md`; self-contained
+ in-repo script `scripts/build_emu.sh`).
- ☐ In-repo device test checklist (the adb tap/swipe/IME sequences used in
Phases 2–3 and Phase 6 are in this plan's phase notes but not a standalone
checklist).
diff --git a/scripts/build_emu.sh b/scripts/build_emu.sh
new file mode 100755
index 0000000..88571f0
--- /dev/null
+++ b/scripts/build_emu.sh
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+# Build (and optionally install) the Pad Android APK for the local emulator.
+#
+# Usage:
+# ./scripts/build_emu.sh # build + install to the connected device
+# ./scripts/build_emu.sh --no-install # build only
+#
+# Output: cmd/pad/pad-emu.apk
+#
+# Prereqs on this workstation (the script checks each and explains what is
+# missing):
+# - JDK 17 (java on PATH)
+# - Go + gogio v0.10.0 (~/go/bin)
+# - Android SDK at ~/android-sdk (platform-tools, build-tools 35.0.0)
+# - debug keystore at ~/.android/debug.keystore
+# apktool is not a prerequisite: if it is missing, the script downloads
+# v3.0.3 to the stable location ~/android-sdk/tools/apktool.jar.
+#
+# Why the apktool detour: gogio does not offer a way to add
+# MANAGE_EXTERNAL_STORAGE to the manifest, and Android 11+ blocks the app's
+# directory listing without it. So we decode the gogio APK, inject the
+# permission, rebuild, and re-sign with the debug key.
+set -euo pipefail
+
+REPO=$(cd "$(dirname "$0")/.." && pwd)
+export ANDROID_HOME=${ANDROID_HOME:-$HOME/android-sdk}
+export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/35.0.0:$HOME/go/bin
+
+INSTALL=1
+[ "${1:-}" = "--no-install" ] && INSTALL=0
+
+die() { echo "ERROR: $*" >&2; exit 1; }
+
+command -v java >/dev/null 2>&1 || die "java not found (need JDK 17)"
+command -v gogio >/dev/null 2>&1 || die "gogio not found (need v0.10.0 in ~/go/bin)"
+command -v apksigner >/dev/null 2>&1 || die "apksigner not found (need $ANDROID_HOME/build-tools/35.0.0)"
+command -v adb >/dev/null 2>&1 || die "adb not found (need $ANDROID_HOME/platform-tools)"
+[ -f "$HOME/.android/debug.keystore" ] || die "debug keystore missing: $HOME/.android/debug.keystore"
+
+# apktool: auto-download to a stable location if missing (never /tmp, which
+# gets wiped between sessions).
+APKTOOL="$ANDROID_HOME/tools/apktool.jar"
+if [ ! -f "$APKTOOL" ]; then
+ echo "=== apktool missing; downloading v3.0.3 to $APKTOOL ==="
+ mkdir -p "$ANDROID_HOME/tools"
+ curl -fsSL -o "$APKTOOL" \
+ https://github.com/iBotPeaches/Apktool/releases/download/v3.0.3/apktool_3.0.3.jar \
+ || die "apktool download failed"
+fi
+
+WORK=$(mktemp -d)
+trap 'rm -rf "$WORK"' EXIT
+
+echo "=== gogio (Go -> APK) ==="
+(cd "$REPO/cmd/pad" && gogio -target android -targetsdk 35 -arch amd64 -o "$WORK/pad-raw.apk" .)
+
+echo "=== apktool decode ==="
+java -jar "$APKTOOL" d "$WORK/pad-raw.apk" -o "$WORK/decoded" -f >/dev/null
+
+echo "=== inject MANAGE_EXTERNAL_STORAGE ==="
+grep -q "MANAGE_EXTERNAL_STORAGE" "$WORK/decoded/AndroidManifest.xml" || \
+ sed -i '0,/\n /dev/null
+
+echo "=== sign ==="
+apksigner sign \
+ --ks "$HOME/.android/debug.keystore" --ks-pass pass:android \
+ --key-pass pass:android --ks-key-alias androiddebugkey \
+ --out "$REPO/cmd/pad/pad-emu.apk" "$WORK/pad-unsigned.apk"
+
+if [ "$INSTALL" = "1" ]; then
+ echo "=== install ==="
+ adb get-state >/dev/null 2>&1 || die "no adb device (start the emulator or plug in a phone)"
+ adb install -r "$REPO/cmd/pad/pad-emu.apk"
+fi
+
+echo "=== DONE: $REPO/cmd/pad/pad-emu.apk ==="