Pad/doc
Greg Pomerantz b1a1719f17 scripts: emu.sh — reliable emulator lifecycle + on-device debug helpers
Replaces the ad-hoc adb/nohup sequences used throughout verification with
one self-contained script: up/down/status, app start|stop|restart, shot,
log, tap, type, cmd (one-shot editor debug command), perf on|off|pull,
push/pull of Notes test files. Encodes the machine-local paths
(Notes/, PadPerf/ profiler + cmd file) in one auditable place.

Reliability findings baked in (each hit during testing):
- The AVD auto-saves a 'default_boot' instant-boot snapshot on clean
  shutdown; a corrupted one makes the emulator segfault during restore
  (device never comes online). up() detects it (process death / no adb
  device within PAD_ONLINE_TIMEOUT) and falls back to a cold boot with
  -no-snapshot-load; the next clean down() re-saves a good snapshot, so
  this self-heals. down() is the only sanctioned stop (clean kill saves
  the snapshot; SIGKILL corrupts it).
- The emulator process identity is ambiguous (launcher vs
  qemu-system-*-headless), and crashpad/netsimd children carry the AVD
  name in their command lines. Liveness and kill therefore use a tight
  signature: comm matches qemu-system-* AND cmdline contains ' -avd
  <AVD>' (never a broad pkill -f pattern — one matched and killed the
  calling shell during testing).
- Before any launch, orphaned qemu instances are cleared (they hold the
  AVD lock and make new launches fail silently); before starting a
  replacement, the old one must be fully gone (lingering device/lock
  causes false success).
- Timeouts: short online window for the snapshot attempt, full
  BOOT_TIMEOUT for the fallback cold boot (~20 s).

Tested: idempotent up, 10 s snapshot-restore boot, forced fallback
(30 s end-to-end, verified correct instance), orphan cleanup, perf/cmd/
push/pull/shot/log on-device. doc/README.md now documents it.
2026-08-16 22:00:56 -04:00
..
architecture.md doc: note GlyphLayout window-relative offset invariant 2026-08-16 20:59:42 -04:00
development_plan.md scripts: self-contained in-repo build_emu.sh (replaces /tmp recipe) 2026-08-16 21:14:25 -04:00
README.md scripts: emu.sh — reliable emulator lifecycle + on-device debug helpers 2026-08-16 22:00:56 -04:00
spec.md perf: default-off in-app profiler + debug scroll jumps; verify scroll perf & clamping 2026-08-16 16:53:28 -04:00

Pad documentation

Three documents, kept at the level of what, why, and invariants — not line-by-line code — so they stay true as the implementation evolves.

Doc What it is
spec.md What the app actually does, measured performance, the code layout, and an explicit list of deferred features.
architecture.md How it works: single-owner concurrency model, channel topology, Frame handoff contract, ownership rules, editor/browser/render internals.
development_plan.md The active plan: completed phases, remaining work, and the on-device observation loop.

Package inventory

Package Role
internal/editor Chunked buffer, line index, virtualized viewport, IME ops, autosave, logic goroutine, state, Frame
internal/browser Directory browsing, search, sort, pagination, browser state machine
internal/ui Element tree, frame layout, renderer (op-based), IME wiring, gestures
internal/perf Default-off performance profiler (per-logic-frame cadence + scroll state → CSV)
internal/io/pool Worker pool (8 workers, 2 priority lanes) + file/dir tasks
internal/test/e2e Harness driving the real Logic + Inspect
cmd/pad main.go (entry), impl_android.go (base path)

Documentation policy

  1. Docs describe invariants and contracts, not code lines. If a document has to change on every refactor, it is too detailed — delete it or raise its level of abstraction. Point-in-time implementation plans are deleted once implemented (the previous *_implementation_plan.md, touch.md, element_model.md, layout_rendering.md, virtual_scroll_render_optimization.md, and conflict_resolution.md were removed in the 2026-08 doc reorganization for this reason).
  2. Unbuilt behavior is not spec'd. Requirements that are not in the code live in spec.md §7 (deferred), never as if they worked.
  3. Code wins. When doc and code disagree, fix the doc.

Build & install (Android, this workstation)

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, debug keystore at ~/.android/debug.keystore. Env vars are set in ~/.bashrc.

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.

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 <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> (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)

Emulator + on-device debug: scripts/emu.sh

All emulator lifecycle and on-device debug operations go through scripts/emu.sh (headless AVD pad_avd, API 35): up / down / status, app start|stop|restart, shot, log, tap X Y, type TEXT, cmd <top|bottom|frac F|dp N> (one-shot editor debug command), perf on|off|pull (in-app profiler), push/pull (Notes files). See the script header for the full list and the device paths it encodes.

Snapshot behavior: the AVD auto-saves an instant-boot snapshot on clean down (restore ~10 s vs ~20 s cold boot). A corrupted snapshot makes the emulator segfault during restore; up detects that and falls back to a cold boot, and the next down re-saves a good snapshot (self-healing). Always stop with down — SIGKILLing the emulator corrupts the snapshot.

App package/activity: pad.pad / org.gioui.GioActivity. Default root directory: /storage/emulated/0/Notes.

On-device observation loop (quick reference)

Gio renders into one GL surface, so the authoritative debug signals are data, not pixels (use scripts/emu.sh log|shot|perf|cmd for the first four):

  • logcat -s pad.pad — app log (errors, limits, recovery; the normal path is quiet by design).
  • input tap|swipe|text — drive the UI (see development_plan.md for the tap coordinates and IME-tap cadence that works). The first tap right after launch/open is sometimes swallowed — re-tap.
  • Autosave debounce is 1 s: wait ~1.6 s before reading a file back from disk after typing.
  • Memory: adb shell dumpsys meminfo pad.pad (watch PSS/RSS; the 3.8 GB emulator OOMs the app above ~2.5 GB RSS).
  • Screenshots are an auxiliary check only — state, logcat, and file diffs are authoritative.