Ensures the app never generates frames without a cause (frame emission
is event-driven; a spinner would burn CPU/battery for the app's entire
idle life). Verified on the emulator and the phone: healthy runs show
ZERO frames across all idle windows (PERF-PRESENT fps < 1).
- perf: rows whose previous frame is >= 100ms away are idle gaps, not
slow frames — flagged in the CSV (new gap column) and kept out of the
logcat summary's latency percentiles (reported as gaps=N maxGap=...).
A frame >= 2s after the previous one flushes the CSV, so a burst's
rows are persisted the moment the next burst starts (an idle tail or
force-stop no longer loses the last burst).
- editor: debug 'open <path>' command (cmd-file poller, perf mode):
opens a file from any page via the same OpenFile path as a browser
tap — deterministic, no pixel tapping.
- e2e: TestNoFramesWhileIdle (browser + editor after load/scroll/find)
asserts the logic emits ZERO frames across an idle window once
settled — the headless contract, in the regular go-test suite.
- scripts/profile_emulator.sh: drives launch, 8s idle, open (seeded
4000-line file), 16s idle, 3 scrolls on a device/emulator; groups the
CSV into phases at idle gaps >= 2s and FAILs on a phase over its frame
budget (1/s spinner exceeds a 16s idle budget; faster ones balloon a
phase or show in PERF-PRESENT). Validated both ways: PASS on a healthy
build (emulator + phone), FAIL on an injected 500ms frame spinner.
- doc: architecture.md §11 updated (gap handling, burst flush, debug
open, frame-regression guard, 2026-08 measurements).