Doc: record Phase 2 on-device IME validation (PASSING) + 3 bugs found

- IME commit path validated on emulator (Gboard/API 35): single-char,
  multi-char (human cadence), deletion (deleteSurroundingText->EditEvent),
  and Unicode all commit correctly with cursor in sync; autosave persists.
- Build recipe scripted (gogio + apktool MANAGE_EXTERNAL_STORAGE + apksigner).
- Bugs: (1) open-file crash via Termux bridge -> fixed (c9c0d47); (2) viewport
  opens at EOF because the opening tap leaks into the editor (open, Phase 3);
  (3) rapid synthetic IME commits desync via per-frame snippet re-push (edge).
This commit is contained in:
Greg Pomerantz 2026-08-16 09:45:51 -04:00
parent c9c0d47f2a
commit 4cfabec7a4

View File

@ -1,7 +1,9 @@
# Development Plan: reach a lean, usable Android text editor
Status: v2, 2026-08-16. Written against the **live** repo `/home/gmp/pad`
(HEAD 03fb638). v1 (the widget-rebuild plan) is superseded — see §12 for why.
Status: v2, 2026-08-16 (Phase 2 on-device IME validation complete). Written
against the **live** repo `/home/gmp/pad`. v1 (the widget-rebuild plan) is
superseded — see §12 for why. Phases 02 done; Phase 3 (large-file + the
viewport-on-open bug) is next.
## 1. Decision summary (updated)
@ -123,16 +125,60 @@ window (the IME discarding and re-anchoring the snippet — a rare case) is
clamped to the window end by the byte-conversion, so it degrades rather than
corrupting; the common within-window path is exact.
### Phase 2 — emulator verification (the observation loop, §9.3)
1. Install Android SDK + NDK + platform-tools; create an x86_64 AVD (16 GB VM,
KVM verified present).
2. Build the APK (`GOOS=android go build ./cmd/pad` with `ANDROID_NDK_HOME`),
install, launch.
3. Drive + observe: state-dump debug flag (add in Phase 1 as a debug build),
logcat, `dumpsys gfxinfo`, screenshot→PIL/OCR.
4. **IME experiment with the AOSP keyboard:** `input swipe` over the keyboard area
and verify behaviorally (did the commit land, non-duplicated, cursor correct?)
via state dump + logcat — this is what confirms §4 works end to end.
### Phase 2 — emulator verification — DONE (IME commit path validated on-device)
Environment (this 16 GB VM): Android SDK + NDK 27 + platform-tools + emulator +
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`).
**On-device IME validation — PASSING** (Gboard, API 35; observed via the gated
`imeDebugLog` trace + logcat + autosaved file diff + screenshots):
- Browser lists `/storage/emulated/0/Notes`; a row tap opens the file in the
in-app editor (see the open-file fix below).
- Editor focus → `key.FocusCmd`/`FocusFilter` → Gboard shows (soft input up).
- **Single-char commit** (tap a Gboard key): one `EditEvent{Range:{c,c},Text}`
`HandleReplaceRange` inserts at the caret; buffer + cursor + autosaved file all
correct.
- **Multi-char commit** (tap keys with human ~0.6 s gaps): cursor advances in sync
(c→c+1→c+2…), each char lands at the right byte offset, file correct.
- **Deletion** (Gboard backspace): routed through the IME as
`EditEvent{Range:{i,i+1},Text:""}` (i.e. `deleteSurroundingText`) →
`HandleReplaceRange` deletes the range correctly.
- **Unicode** content (`héllo wörld 日本語`) displays and the buffer stays
consistent through edits around it.
- Conclusion: §4 (the IME wiring gap) works end to end for a real IME's commit
path. Swipe-typing and autocorrect use the *same* commit path
(`commitText`/`setComposingText` → `EditEvent`), so they are covered by this;
a real-device final sign-off with a swipe/autocorrect IME is still worth doing.
**Bugs found + fixed on-device (Phase 2):**
1. **Open-file crash (fixed, `c9c0d47`):** browser-row taps were routed through
the Android Termux bridge (`ui.OpenFile = openfunc`), which built a `file://`
Intent URI and crashed on Android 7+ with `FileUriExposedException`, and it
bypassed the editor entirely. Fix: `ui.OpenFile` now calls the in-app
`editor.OpenFile(path)`; the Termux bridge is retained as a dormant
`TheState.open` hook.
2. **Viewport opens at EOF (open bug, Phase 3):** after a file opens, the
*opening* tap leaks into the now-visible editor and `SetCursorFromPoint`/scroll
moves the viewport to the tapped row (below a short file's content) → the
content area renders blank (`VisibleByteRange` lands on a past-EOF visual
line; `visibleContent (0)`). Workaround used during testing: swipe to top.
Root cause is the tap that switches `page=EditorPage` also being delivered as an
editor tap. Needs: ignore the opening tap in the editor (or clamp
`SetCursorFromPoint`/scroll so a short file that fits the viewport never
scrolls).
3. **Rapid synthetic IME commits desync (edge case):** `adb shell input text
"…"` fires per-char commits faster than a frame; the per-frame `SnippetCmd`
re-push resets the IME's cursor, so fast commits interleave/corrupt
(observed `" IME123"``E123IM…`). Real typing (human cadence) stays in sync
(validated above). Robustness note for Phase 3: only re-push the snippet when
it actually changes, or suppress re-push mid-composition.
Observation loop that worked (no state-dump flag needed in the end): logcat
(`imeDebugLog` + `HandleKeyDown` + `VisibleByteRange`), autosaved-file diff via
`adb shell cat`, and screenshots (vision, auxiliary).
### Phase 3 — large-file validation + honest size limit
1. Open a real **10 MB** file via the chunked buffer; verify smooth scroll + edit
@ -255,6 +301,10 @@ within ~1 s. Repo: `go test -race ./...` green.
to stay fully in memory (the ~50 MB target is borderline; see §6).
- **AOSP keyboard** is a proxy for real IMEs (Gboard, etc.); final swipe/autocorrect
sign-off needs a real device with a real IME.
- **Viewport opens at EOF** (Phase 2, open): the opening tap leaks into the editor
and scrolls a short file past its content → blank editor until you scroll to top.
Blocks a clean first impression; fix in Phase 3 (ignore the opening tap / clamp
scroll for files that fit the viewport). See §5 Phase 2 bugs.
## 12. Why v1 (widget rebuild) is now a fallback, not the plan
v1 was written against a stale snapshot (May 31) and concluded "delete