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. |
||
|---|---|---|
| .. | ||
| architecture.md | ||
| development_plan.md | ||
| README.md | ||
| spec.md | ||
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
- 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, andconflict_resolution.mdwere removed in the 2026-08 doc reorganization for this reason). - Unbuilt behavior is not spec'd. Requirements that are not in the code
live in
spec.md§7 (deferred), never as if they worked. - 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):
gogio -target android -targetsdk 35 -arch amd64→ raw APK (fromcmd/pad/)apktool d→ decode- Inject
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>(without it, Android 11+ blocks listing/storage/emulated/0) apktool b→ rebuildapksigner signwith the debug key →cmd/pad/pad-emu.apkadb 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 (seedevelopment_plan.mdfor 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.