RUST.md, IRIS.md, IRIS_TODO.md, DECISIONS.md: record I5's frame report and holddrag results

FrameReport gave a real, measured on-device number (frames=34,
janky%=61.76, p50=26.5ms p90=48.0ms p99=98.1ms worst=98.1ms) and
long-press-then-drag-to-select is now confirmed on-device (logcat plus a
screenshot of the highlighted selection). Neither closes I5's box to [x]
yet: the frame number is real but not the clean single 24-swipe loop
comparable to Compose's, because gestures against this checkout's
EMU_GPU=software emulator intermittently delivered zero touch input this
session -- a new, separately named finding (candidate cause: the
emulator's own software rasterisation measured at ~78% of a CPU core
continuously), not yet root-caused. DECISIONS.md's DEFERRED item is
updated with these numbers rather than a decision made here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-05 15:00:29 -04:00
1 parent 470f8e5019
commit 1e7b1cddb7
4 files changed
+292 -89

No files matched your search

+23 -15
View File
@@ -66,18 +66,26 @@ marked **DEFERRED** are ones the agent chose not to decide alone.
pieces and I5's Android integration are all done; next is giving iris
its own frame-timing report so item 3 below can be decided by a number.
- **DEFERRED — whether to commit to iris over Masonry for `ai-app`.**
Updated 2026-09-05 with what's now known: iris's Android integration is
built and confirmed working on a real device (real server, real
scrolling, real touch-drag pan, tap-by-name accessibility — RUST.md's I5
box), while Masonry's own touch-scroll on Android was already found
entirely absent (E2). What's still missing on **both** sides is a
render-time number — not because iris doesn't work, but because
`dumpsys gfxinfo` cannot see a `SurfaceView`'s GPU-drawn frames at all,
so the `transcript-bench.sh`-style comparison this recommendation wanted
produced a real number for Compose (8.96% janky, 99th percentile 150ms,
same emulator/session) and none at all for iris. The choice in front of
Iris: decide now on the structural-plus-functional case already made
(iris works end-to-end where Masonry's scroll gesture doesn't exist at
all on Android), or wait for iris to grow its own frame-timing
instrumentation first so the comparison can be a number rather than a
qualitative one. RUST.md's "Recommendation" item 3 has the full account.
Updated 2026-09-05 (later the same day) with what's now known: iris has
its own frame-timing report now (`FrameReport`) and a real number from
it, captured from a genuine on-device touch-drag —
`frames=34 janky%=61.76 p50=26.5ms p90=48.0ms p99=98.1ms worst=98.1ms`
— plus a confirmed on-device long-press-then-drag-to-select (logcat and
a screenshot of the highlighted selection). It is **not yet the clean,
comparable number** the recommendation wants: gestures against this
checkout's `EMU_GPU=software` emulator intermittently delivered zero
touch input during this session, reproducibly, for a reason named but
not yet confirmed (the emulator's own software rasterisation measured
at ~78% of a CPU core continuously — a plausible source of input
backlog, not yet isolated with a sampler running during a failing
gesture). The Compose figure quoted for comparison (8.96% janky, 99th
percentile 150ms) is the same one already recorded earlier 2026-09-05,
not re-taken this pass, and against a different sandbox session's
content — so the two numbers share configuration, not identical data.
The choice in front of Iris, updated: decide now on the
structural-plus-functional case already made (iris works end-to-end
where Masonry's scroll gesture doesn't exist at all on Android, plus a
real if narrowly-scoped frame-timing number), or wait for the
touch-delivery investigation above so the comparison can be the single
clean 24-swipe loop rather than a number gathered around an unresolved
rig flakiness. RUST.md's "Recommendation" item 3 has the full account.
+43
View File
@@ -8,6 +8,49 @@ capability that moved. Small and trivial changes do not go here.
An entry gives the date, what changed, why, and a short before/after where
it helps judge the change without the session that made it. Newest first.
## 2026-09-05 (later the same day): `iris_core::FrameReport` (RUST.md's I5 box)
New public type, `iris_core::FrameReport` (re-exported from `iris_core`'s
`render` module alongside `FrameStats` and `JANK_THRESHOLD`). Why: `dumpsys
gfxinfo` cannot see a `SurfaceView`'s own GPU-drawn frames at all, so a
`wgpu`-rendered iris screen had no way to ask "was this smooth" the way
Compose's own in-app render report already can -- item 3 of RUST.md's
recommendation was stuck on a one-sided number for exactly this reason.
`FrameReport::record(elapsed: Duration)` is called once per frame (wired
into `android/view.rs`'s `render()`, wrapping the same span from redraw
start to after `queue.submit`+`present()` that Compose's report and
`gfxinfo` both count) and writes into a fixed 4096-entry ring -- no
allocation on the hot path. `FrameReport::report() -> Option<FrameStats>`
gives total frames, janky % (over `JANK_THRESHOLD`, the same 16.7ms 60Hz
budget `gfxinfo` uses), P50/P90/P99 and the worst; `None` if nothing has
been recorded since the last `reset()`, not a zeroed report that would
read as a real measurement. `FrameStats`'s `Display` line says plainly
that it measures up to `present()` being called, not GPU/compositor
completion, since wgpu's `present()` isn't fenced against either.
`AndroidUiState` gained a `pub frame_report: FrameReport` field --
anything with `HasAndroidUiState` can now read or reset it. Before this,
there was no way to ask iris's own render path how long a frame took at
all, on any backend.
Before/after, for a caller that already has `ui_state: &AndroidUiState`:
```rust
// before: no such question could be asked
// after:
match ui_state.frame_report.report() {
Some(stats) => log::info!("iris frame report: {stats}"),
None => log::info!("iris frame report: no frames recorded yet"),
}
ui_state.frame_report.reset(); // via android_state_mut()
```
`iris-android-app`'s transcript screen exposes this as two named,
tappable controls ("Frame report", "Reset frame report") rather than
requiring a caller to wire its own UI -- see `transcript_client.rs`'s
`frame_report_controls`.
## 2026-09-05: `Tasks::redraw_handle` (RUST.md's I5 Android integration)
New public method on `iris::task::Tasks`, `redraw_handle(&self) ->
+46 -19
View File
@@ -186,25 +186,52 @@ order and what "done" looks like. Tick and date them in place.
requirement, fixed by routing through `View::post_delayed` — see
`IRIS.md`'s `Tasks::redraw_handle` entry). See RUST.md's I5 box,
"The Android integration, done 2026-09-05" for the full account.
- [ ] **A render-time number for iris, comparable to Compose's
`transcript-bench.sh` report.** `dumpsys gfxinfo` cannot see a
`SurfaceView`'s own GPU-drawn frames at all (confirmed: 0 frames
reported across a gesture loop that visibly scrolled), and a
`dumpsys SurfaceFlinger --latency` fallback returned no per-frame
history either on this Android version's BLAST compositor. What's
needed is frame-timing instrumentation inside iris itself — a report
the way `UiRenderState::take_counters`/`AccessTree::take_rebuilds`
already expose counters, extended to timing. This is the one number
RUST.md's recommendation (item 3) is still waiting on.
- [ ] **Long-press-then-drag-to-select was not independently driven
on-device.** `ui-trace`'s two gesture primitives (`tap`, `swipe X1 Y1
X2 Y2 MS`) cannot produce "hold stationary for `LONG_PRESS`, then drag
without lifting" — `swipe` interpolates motion across its whole
duration from the start. Needs either a new `ui-trace` action (a
genuine hold-then-drag primitive) or raw multi-step `MotionEvent`
injection. `DragArbiter`'s own unit tests already cover this exact
sequence against a synthetic clock (`iris/src/sense.rs`), which is why
this is "not independently driven on-device" rather than "unverified."
- [x] **A render-time number for iris, comparable to Compose's
`transcript-bench.sh` report — instrumentation done and a real number
obtained, 2026-09-05 (later the same day); the clean comparable loop
is not.** `iris_core::FrameReport` (`iris/core/src/render/
frame_report.rs`, `IRIS.md`'s new entry) times every frame from
`render()`'s redraw start to after `queue.submit`+`present()`, exposed
as two named on-screen controls ("Frame report", "Reset frame
report"). Driven against a real on-device touch-drag it read
`frames=34 janky%=61.76 p50=26.5ms p90=48.0ms p99=98.1ms
worst=98.1ms` — real, not inferred, but accumulated across several
gestures rather than one clean 24-swipe loop, because of the new
finding below. See RUST.md's I5 box, "Update, 2026-09-05, later the
same day" for the full account.
- [ ] **New, 2026-09-05: intermittent touch delivery to iris's
`SurfaceView` under this checkout's `EMU_GPU=software` emulator.**
The same swipe coordinates, confirmed (by scanning a screenshot
column for the first non-black pixel) to sit over real row text,
sometimes produced 30+ real frames and a screenshot diff and
sometimes produced zero of either, across otherwise-identical
`ui-trace` invocations. Not the already-understood "already at that
scroll edge" case (reproduced with content confirmed taller than the
viewport, in both directions). Leading candidate, not yet confirmed:
this checkout's emulator was independently observed at ~78% of one
CPU core, continuously, while idle on-screen — SwiftShader's software
rasterisation is CPU-bound by design, and a synthetic touch competing
with that load for delivery is plausible but unmeasured *during* a
failing gesture (the standing rule against diagnosing from
after-the-fact measurements applies here). Needs a sampler (load,
`dumpsys input`, a `-i 0` `ui-trace` capture) running while a failing
gesture is driven, and ideally a comparison under `-gpu host` (real
Vulkan) to see whether it is specific to software rendering. This is
what blocks the clean, comparable 24-swipe loop above.
- [x] **Long-press-then-drag-to-select — confirmed on-device, 2026-09-05
(later the same day).** `ui-trace` gained a `holddrag X1 Y1 X2 Y2
HOLD_MS MOVE_MS` action (`emulator-tools`, additive, extends the same
`MotionEvent`/`injectInputEvent` mechanism `swipe` already used):
press, hold past `LONG_PRESS`, move, release, as one continuous touch.
Driven against a real row (`holddrag 300 1850 300 2050 600 300`) it
produced `iris selection: begin at row ...` then a sequence of
`iris selection: extend to row ...` log lines
(`transcript-ui/src/selection.rs`, a new small `log` dependency since
selection has no accessibility label of its own yet — see the next
item), and a screenshot taken right after shows the expected
highlighted selection spanning multiple rows. `DragArbiter`'s own
unit tests already covered this sequence against a synthetic clock;
this is the first time it has been driven by a real device touch.
- [x] **Touch-drag panning over a row's own rendered text — done,
2026-09-05.** `row.rs` used to register `CursorSense::click_or_drag()`
on each row's `TextEdit` for cross-row selection; `TextEdit::draw`'s
+180 -55
View File
@@ -36,23 +36,35 @@ session spending an afternoon on them again.
## Where things stand (2026-09-05)
- **I5's Android integration is done and measured, 2026-09-05 (still ticked
`[~]`, not `[x]` -- see I5's own box for exactly why).** The transcript
screen runs on-device against a real `ai-server`, with real scrolling,
real touch-drag panning and tap-by-name accessibility all confirmed by
screenshot/log evidence on this checkout's emulator. What the
recommendation still lacks is a render-time number for iris comparable
to Compose's `transcript-bench.sh` report -- not because the screen
doesn't work, but because `dumpsys gfxinfo` cannot see a `SurfaceView`'s
own GPU-drawn frames at all, and iris has no frame-timing
instrumentation of its own to ask instead. Two real, previously-unknown
bugs were found and fixed getting here (a missing `INTERNET` permission,
and a background-thread redraw request that crashed the process via a
`Looper` requirement neither this box nor `Tasks::redraw_handle`'s
design had anticipated) -- both in I5's own box, both in `IRIS.md`.
Next: give iris a render-time report of its own, the one piece left
before the iris-vs-Masonry recommendation can be decided by a number
rather than by structure alone.
- **iris now has its own frame-timing report and a confirmed on-device
long-press-drag, 2026-09-05, later the same day (I5's box still `[~]`,
not `[x]` -- see I5's own box for exactly why).** `iris_core::FrameReport`
(new) times every frame from `render()`'s redraw start to after
`queue.submit`+`present()`, exposed as two named on-screen controls, and
a new `ui-trace holddrag` action (in `emulator-tools`) can finally
produce a real hold-then-drag touch sequence. Both were driven for
real: long-press-then-drag-to-select is confirmed on-device (logcat plus
a screenshot of the highlighted selection), and `FrameReport` captured a
real number from a real touch-drag (`frames=34 janky%=61.76 p50=26.5ms
p90=48.0ms p99=98.1ms worst=98.1ms`). **What's still missing**: a clean,
single 24-swipe loop comparable to Compose's own number -- gestures
against this checkout's `EMU_GPU=software` emulator intermittently
delivered zero touch input during this pass, for a reason named but not
yet confirmed (candidate: the emulator's own software rasterisation,
measured at ~78% of a CPU core continuously, starving synthetic touch
delivery under load). Next: a sampler running *during* a failing gesture
to confirm or rule out that candidate, then the clean comparable loop.
I5's own box, "Update, 2026-09-05, later the same day" has the full
account.
- **I5's Android integration is done and measured, 2026-09-05.** The
transcript screen runs on-device against a real `ai-server`, with real
scrolling, real touch-drag panning and tap-by-name accessibility all
confirmed by screenshot/log evidence on this checkout's emulator. Two
real, previously-unknown bugs were found and fixed getting here (a
missing `INTERNET` permission, and a background-thread redraw request
that crashed the process via a `Looper` requirement neither this box nor
`Tasks::redraw_handle`'s design had anticipated) -- both in I5's own box,
both in `IRIS.md`.
- **Design choices for the two pieces before this are summarised in
`DECISIONS.md`** at the repo root, which is the file Iris reads for
choices made without her.
@@ -699,12 +711,35 @@ light" has a knob inside the same stack.
programmatically (I3's benchmark plus I5's on-device screenshot
evidence) — three structural points and one functional one in iris's
favour, still with no opposing *or* supporting render-time measurement
on either side. **What Iris needs to weigh this**: whether "iris works
and Masonry's Android scroll path is absent entirely" is enough to
decide without a number, or whether the frame-timing work above is
worth doing first — a product/tradeoff call, not a technical one, so it
is left to her rather than decided here (DECISIONS.md's DEFERRED
item).
on either side.
**Update, 2026-09-05, later the same day: iris now has a render-time
report of its own, and a real number from it, but not yet the clean
comparison item 3 needs.** `iris_core::FrameReport` (new,
`iris/core/src/render/frame_report.rs`) is exactly the follow-on work
named above — a per-frame wall-time ring exposed as two named on-screen
controls, unit tested (6 tests over the ring/percentile math). Driven
for real against a real touch-drag on this checkout's emulator, it
read `frames=34 janky%=61.76 p50=26.5ms p90=48.0ms p99=98.1ms
worst=98.1ms` — a genuine measurement through iris's own render path,
not inferred. **It is not yet the comparable number**, for a newly
found and separately named reason (I5's own box, "Update, 2026-09-05,
later the same day"): gestures against this checkout's
`EMU_GPU=software` emulator intermittently delivered zero touch input
during this pass — reproducible, but not yet root-caused past one
candidate (the emulator's own software rasterisation measured at ~78%
of a CPU core continuously, a plausible source of input backlog, not
yet confirmed with a sampler running during a failing gesture). So
item 3 still cannot be closed by a clean number, now for a narrower and
more tractable reason than before: the instrumentation exists and
works, and what remains is making the emulator rig deliver touch input
reliably enough to run the comparable loop. **What Iris needs to
weigh, updated**: whether "iris works, Masonry's Android scroll path is
absent entirely, and iris's own frame-timing report is real and
working" is enough to decide without the final clean number, or
whether to wait for the touch-delivery investigation above — still a
product/tradeoff call, left to her (`DECISIONS.md`'s DEFERRED item,
updated with this session's numbers).
4. Then the shell (E3), the desktop window (E4) and the packaging (E5),
which do not depend on the choice.
@@ -2309,17 +2344,23 @@ silently on real hardware.
time it was hit (mid-session, after adding an unrelated temporary
log line forced a dev rebuild) and cost real time to separate from
the actual touch-dispatch question being chased at the time.
- [~] **I5 — the transcript screen in iris (2026-09-05). The widget-tree
half and the Android integration are both built and confirmed
working on-device (real server, real scrolling, real touch-drag
pan, tap-by-name); still `[~]` rather than `[x]` because the one
thing the recommendation actually wants -- a render-time number for
iris comparable to Compose's -- could not be produced on this pass,
for a reason named precisely rather than left vague: `dumpsys
gfxinfo` cannot see a `SurfaceView`'s own GPU-drawn frames at all,
and iris has no frame-timing instrumentation of its own yet to ask
instead. See "Measurements taken" and "What remains" near the end of
this box.**
- [~] **I5 — the transcript screen in iris (2026-09-05, updated later the
same day). The widget-tree half and the Android integration are
both built and confirmed working on-device (real server, real
scrolling, real touch-drag pan, tap-by-name), iris now has its own
frame-timing instrumentation (`FrameReport`) and produced a real,
measured number from a real on-device touch-drag, and long-press-
then-drag-to-select is now confirmed on-device too (both by logcat
and by a screenshot showing the highlighted selection); still `[~]`
rather than `[x]` because the frame-time number obtained is not the
clean, single 24-swipe loop comparable to Compose's -- gestures
against this checkout's `EMU_GPU=software` emulator intermittently
produced zero frames for reasons not yet isolated (candidate: the
emulator's own software rasterisation measured at ~78% of a CPU
core, continuously, which may be starving synthetic touch delivery
under load -- not yet confirmed with a sampler running during the
failing gesture). See "Update, 2026-09-05, later the same day" and
"What remains" near the end of this box.**
**Where it lives.** `iris/transcript-ui/` (new workspace member,
`[lib]`), the same shape as `iris/tabs-ui`: generic over `Rsc:
@@ -2699,15 +2740,99 @@ silently on real hardware.
a synthetic clock, which is why the mechanism is trusted enough to
call "not independently driven on-device" rather than "unverified."
**Update, 2026-09-05, later the same day: (b) has a real iris number
now, and (c) is confirmed on-device.** Both needed new tooling built
this pass, recorded in `DECISIONS.md`: `iris_core::FrameReport`
(`iris/core/src/render/frame_report.rs`) times each frame from
`render()`'s redraw start to after `queue.submit`+`present()` into a
fixed 4096-entry ring, exposed as two named controls on the
transcript screen ("Frame report", "Reset frame report",
`iris/android-app/src/transcript_client.rs`) logged under this
crate's fixed `android_logger` tag; and `ui-trace` gained a
`holddrag X1 Y1 X2 Y2 HOLD_MS MOVE_MS` action in `emulator-tools`
(press, hold, move, release as one continuous touch via the same
`MotionEvent`/`injectInputEvent` mechanism `swipe` already used),
closing the exact gap named above.
(b), continued: **a real iris number exists, but it is not the clean
24-swipe `transcript-bench.sh`-equivalent loop this box originally
wanted, for a reason worth recording precisely.** Driving the
gesture loop against a freshly-restarted app repeatedly produced
**zero** frames recorded (both by `gfxinfo`, already known, and now
also by `FrameReport` itself) even though the coordinates were
confirmed on-screen to sit over real row text (measured by scanning
a screenshot column for the first non-black pixel, not guessed) --
while the *same* coordinates driven a few commands later, or
combined into a slightly different sequence, sometimes produced 30+
real frames and a genuine screenshot diff. This is not the earlier,
already-understood "already at that scroll edge" case (AGENTS.md's
own note) -- it reproduced with fresh content confirmed taller than
the viewport, in both scroll directions, inconsistently across
otherwise-identical commands. The one measured correlate: this
checkout's own `EMU_GPU=software` emulator was independently seen at
**~78% of one CPU core, continuously**, while idle on-screen (`ps
aux` mid-session) -- SwiftShader's software rasterisation is
CPU-bound by design (AGENTS.md's Vulkan-in-the-emulator section), so
a synthetic touch's delivery to the SurfaceView competing with that
load is the leading candidate, not yet confirmed with a sampler
running *during* the gesture (the standing rule against diagnosing
from measurements taken after the fact applies here and this pass
did not have time to build that sampler). Recorded as a new,
distinct, unresolved finding in `IRIS_TODO.md` rather than folded
into the already-closed "no `hold`-then-drag primitive" gap.
**The number obtained, honestly scoped**: tapping "Frame report"
immediately after a run that *did* produce real scrolling frames
(screenshots differ, confirmed by hash) read
`frames=34 janky%=61.76 p50=26.5ms p90=48.0ms p99=98.1ms
worst=98.1ms` -- real, measured wall-clock time through iris's own
render path from a real on-device touch-drag, not a synthetic
probe, but accumulated across several swipe gestures across
multiple `ui-trace record` invocations rather than one clean
24-swipe loop, so it is **not directly comparable** to the Compose
figure below in scale, only in kind. Given the CPU contention
candidate above, a high jank percentage here is expected under
software rendering and should not be read as iris's number on real
hardware. The Compose figure quoted for reference
(**8.96% janky frames, 99th percentile 150ms, 212 frames rendered**)
is the same measurement this box already recorded on 2026-09-05
earlier the same day, under the same `EMU_GPU=software` config on
this same checkout's emulator -- **not re-taken this pass** (the
session's time went to building the two rigs above and diagnosing
the flakiness instead), and against a *different* sandbox session
(40 messages, id `8920378e7167ebcd`) than this pass's own (120
messages, id `c76b71d017a54589`), so the two numbers share
configuration but not identical content -- said plainly rather than
presented as a matched pair.
(c), continued: **confirmed on-device, by both routes the task
asked for.** `ui-trace record --do "holddrag 300 1850 300 2050 600
300"` (a 600ms hold, comfortably past `DragArbiter`'s 500ms
`LONG_PRESS`, then a 300ms move) against a row's real text produced,
in order: `iris selection: begin at row 3165`, then a sequence of
`iris selection: extend to row ...` lines as the drag crossed row
boundaries -- logged from `transcript-ui/src/selection.rs`'s
`Selection::drag` (new `log` dependency, smallest addition since
selection has no accessibility label of its own yet, per
`IRIS_TODO.md`'s existing gap). A screenshot taken right after shows
the expected highlighted selection spanning multiple rows,
confirming the mechanism visually as well as in the log. This is
the first time `DragArbiter`'s pan-vs-select decision has been
driven by a *real* Android touch sequence rather than only its own
synthetic-clock unit tests.
**What remains, named rather than silently dropped (also in
IRIS_TODO.md, dated 2026-09-05):**
- **A real per-app frame-time number for iris** -- (b) above. Needs
`iris` to grow its own render-report/frame-timing instrumentation
(what the Compose app's copy-button report already gives) before
any `dumpsys`-based comparison can go further than functional.
- **Long-press-then-drag-to-select, on-device** -- (c) above. Needs
a driving primitive this pass's tools do not have.
- **Intermittent touch delivery under `EMU_GPU=software` CPU load**
-- new finding above. Needs a sampler running *during* a failing
gesture (load, `dumpsys input`, a frame-by-frame `ui-trace`
capture at `-i 0`) rather than another guess after the fact, and
ideally a comparison against `-gpu host` (real Vulkan, but shared
with whichever GPU config a peer session's emulator already
holds) to see whether it is specific to software rendering.
- **A clean, single 24-swipe `transcript-bench.sh`-equivalent
iris number** -- blocked on the above; the number this pass got is
real but not that clean run.
- **Row-level accessibility names** -- behaviour 6, I5's own writeup
above.
- **A tappable link and a code-span background chip** -- behaviour 2.
@@ -2720,21 +2845,21 @@ silently on real hardware.
own unit tests), not by a dedicated repro this pass.
**Net for the recommendation.** Item 3 ("decide when the transcript
screen exists in both, from the measurements") still cannot be
decided by a render-time number on either side -- E2 could not
produce one for Masonry (no working touch-scroll to measure), and
this pass could not produce one for iris either, now for a different
and more fixable reason: `iris` itself has no frame-timing
instrumentation yet, not that the screen doesn't work. What *can* be
said, updating E2's own conclusion further: iris now has a real,
working Android integration -- a real server, a real scrolling
transcript, real touch-drag panning, tap-by-name accessibility, all
confirmed on-device -- plus the two structural points E2 already
found Masonry unable to reach at all (cross-row selection, true
per-span inline rich text). Whether iris's *smoothness* matches or
beats Compose's is the one question actually left for whoever picks
up "add frame-timing instrumentation to iris" next; DECISIONS.md's
DEFERRED item is updated with exactly this gap.
screen exists in both, from the measurements") now has a real number
on the iris side for the first time -- `FrameReport` works, is unit
tested (6 tests over the ring/percentile math), and captured a real
on-device touch-drag's timing -- but that number is scoped narrowly
(accumulated over several gestures, not one comparable loop) because
of the intermittent-touch-delivery finding above, so it still cannot
be read against Compose's 8.96%/150ms figure as a clean comparison.
What *can* be said, updating the account further: iris's Android
integration, its frame-timing instrumentation, and its long-press
selection have all now been exercised by real on-device touch input
end to end (not only unit tests), on top of the structural points
E2 already found Masonry unable to reach at all (cross-row
selection, true per-span inline rich text). `DECISIONS.md`'s
DEFERRED item is updated with this session's numbers and the
touch-delivery caveat rather than a decision made here.
## For the next agent