docs: record the streaming-rebuild fix, its numbers, and the new scripts
RUST.md's P0 box gets the fix, the before/after streaming-phase numbers (with their caveats), the build-apk.sh/run-bench.sh scripts, and what the dropout-fix pass's three remaining verifications are blocked on (the sandbox ai-server currently fails to build, unrelated to this change). IRIS.md gets the List::replace_back/clear and TranscriptScreen::apply API entries. AGENTS.md's rigs section gets one sentence on each script. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
5655fa8093
commit
46d3a6fd41
3 files changed
+199
-16
No files matched your search
+145
-16
@@ -36,6 +36,20 @@ session spending an afternoon on them again.
|
||||
|
||||
## Where things stand (2026-09-05)
|
||||
|
||||
- **Streaming no longer costs a full rebuild** (P0's box, "Streaming no
|
||||
longer costs a full rebuild" subsection): `iris::widget::List::
|
||||
replace_back`/`clear` plus `transcript_ui::TranscriptScreen::apply`
|
||||
replace the "refold + rebuild the whole ~3,200-row tree per event" path
|
||||
in all three clients. Worst/p99 frame time in the streaming phase
|
||||
dropped roughly 3x on this checkout's emulator (see the box for the
|
||||
exact numbers and their caveats). Blocked and not done this same pass:
|
||||
the three `iris-scroll.sh` runs, the host-GPU `FrameReport` retake, and
|
||||
the `EMU_GPU=software` cold boot the dropout-fix pass left open --
|
||||
`app/ui-sandbox.sh`'s `ai-server` currently fails to build
|
||||
(`event_model::Event::LimitReached` missing, unrelated to this pass's
|
||||
diff). Two new scripts, `iris/android-app/build-apk.sh` and
|
||||
`iris/android-app/run-bench.sh`, now do the build/install/tap/read-report
|
||||
cycle that used to be typed out by hand each time.
|
||||
- **The intermittent touch-scroll dropout is root-caused and fixed,
|
||||
2026-09-05.** Not the previously-suspected coalesced first
|
||||
`ACTION_MOVE` (ruled out) -- a gesture's `ACTION_DOWN` can land on a
|
||||
@@ -1515,20 +1529,23 @@ accepted.
|
||||
the bare REST fetch (no live stream yet) looked perfect on its own,
|
||||
and only *resuming* a stream after it exposed the seam.
|
||||
|
||||
**Deliberately left simple, and why** (`app.rs`'s module doc has the
|
||||
full account): every incoming SSE event refolds the session's whole
|
||||
item list and rebuilds the entire right-hand widget tree from
|
||||
scratch, rather than reaching for `TranscriptScreen::push_row`'s
|
||||
incremental append -- `push_row` can only add a new row, and a
|
||||
streaming reply is exactly a row whose text keeps changing after it
|
||||
first appears. Fine at the size a desktop session's conversation
|
||||
is; wrong for a long, fast-streaming one, and the real fix needs
|
||||
`transcript-ui` to expose updating a row already on screen, which it
|
||||
does not yet. The composer's in-progress text is saved and restored
|
||||
across a rebuild so a reply streaming in while the reader is typing
|
||||
a followup doesn't erase it. No history paging (I3's job, reused
|
||||
as-is if this becomes permanent) and no scroll-position preservation
|
||||
across a rebuild -- both named rather than silently missing.
|
||||
**Deliberately left simple at the time, later fixed** (`app.rs`'s
|
||||
module doc has the full account): every incoming SSE event used to
|
||||
refold the session's whole item list and rebuild the entire
|
||||
right-hand widget tree from scratch on every event, rather than
|
||||
reaching for `TranscriptScreen::push_row`'s incremental append --
|
||||
`push_row` could only add a new row, and a streaming reply is
|
||||
exactly a row whose text keeps changing after it first appears.
|
||||
Fine at the size a desktop session's conversation is; wrong for a
|
||||
long, fast-streaming one -- fixed below (this same box's "Streaming
|
||||
no longer costs a full rebuild" entry) by giving `transcript-ui` a
|
||||
`TranscriptScreen::apply` that updates a row already on screen
|
||||
instead of rebuilding every row around it. `rebuild_transcript`
|
||||
still runs the whole tree once, for a freshly loaded/selected
|
||||
session and for `apply`'s own rare full-rebuild fallback. No history
|
||||
paging (I3's job, reused as-is if this becomes permanent) and no
|
||||
scroll-position preservation across a rebuild -- both still named
|
||||
rather than silently missing, and neither depends on the fix below.
|
||||
Background network I/O runs on plain `std::thread`s reporting back
|
||||
through winit's `EventLoopProxy<AppEvent>` rather than iris's own
|
||||
`Tasks`/`task_on`, because `Tasks` only requests a redraw once after
|
||||
@@ -3581,8 +3598,12 @@ device.
|
||||
400 fixture events replay at 20/s through `fold_event` -- the same
|
||||
fold path a live SSE frame takes in `transcript_client.rs`'s own
|
||||
`apply_event` -- each one triggering `rebuild_transcript`'s full
|
||||
`transcript_ui::build_tree` rebuild, same tradeoff as
|
||||
`TranscriptClient`/`desktop-app`. A battery sampler runs
|
||||
`transcript_ui::build_tree` rebuild at the time this box was
|
||||
written, same tradeoff as `TranscriptClient`/`desktop-app`. **Fixed
|
||||
2026-09-05, later the same day**: all three now call
|
||||
`TranscriptScreen::apply` instead -- see this same section's
|
||||
"Streaming no longer costs a full rebuild" entry below for the
|
||||
before/after numbers. A battery sampler runs
|
||||
concurrently on its own `tokio::spawn`d task (not through
|
||||
`ctx.update`, since a JNI battery read needs no widget-tree access),
|
||||
attaching whichever thread it runs on via a stored `JavaVM` --
|
||||
@@ -3715,6 +3736,114 @@ device.
|
||||
branch was using (`iris/core/src/render/mod.rs`,
|
||||
`iris/src/android/render.rs`, `iris/src/default/render.rs`).
|
||||
|
||||
**Streaming no longer costs a full rebuild, 2026-09-05.** The gap
|
||||
named above and in E4/I5 (`push_row` can only append; a streaming
|
||||
reply is a row that keeps *changing* after it appears) is closed:
|
||||
`iris::widget::List` gained `replace_back` (swap the last row's
|
||||
widget in place, same slot, so a pinned-to-newest list stays pinned
|
||||
and an off-screen replace moves nothing on screen -- two new unit
|
||||
tests, `replacing_the_last_row_stays_pinned_to_the_bottom` and
|
||||
`replacing_the_last_row_out_of_view_does_not_move_visible_rows` in
|
||||
`iris/src/widget/list.rs`) and `clear` (drop every row, the fallback
|
||||
path). `transcript_ui::TranscriptScreen::apply(rsc, old_items,
|
||||
new_items)` diffs `group_tool_runs(old)`/`group_tool_runs(new)`
|
||||
(pure bookkeeping, no widget built doing it) and picks the cheapest
|
||||
update: unchanged (no-op), pure append (`push_row`, same as before),
|
||||
the common streaming case -- only the last row's content changed --
|
||||
rebuilds just that one row and swaps it in with `replace_back`, or
|
||||
(rare: `group_tool_runs` regrouping a row before the tail) a full
|
||||
`List::clear` rebuild, counted by `TranscriptScreen::take_rebuilds()`.
|
||||
Seven new unit tests in `transcript-ui/src/lib.rs`'s `diff_tests`
|
||||
cover all three cases directly against synthetic `Vec<FoldedRow>`s
|
||||
(no widget/Rsc needed for the decision itself). `bench_client.rs`,
|
||||
`transcript_client.rs` and `desktop-app/app.rs` all call `apply` now
|
||||
instead of rebuilding per event; `IRIS.md`'s 2026-09-05 entry has
|
||||
the full API account. `TextEditCtx` also gained `set_with_spans`
|
||||
(`iris/src/widget/text/edit.rs`) -- `set()` plus a fresh span list
|
||||
in one call, since a streamed row's re-rendered markdown needs both
|
||||
to land together.
|
||||
|
||||
**Two new scripts, `iris/android-app/build-apk.sh` and
|
||||
`iris/android-app/run-bench.sh`**, written this pass after repeating
|
||||
the ANDROID_HOME/NDK-export/`cargo ndk`/Gradle-release/keystore/
|
||||
apksigner incantation by hand one too many times. `build-apk.sh
|
||||
[debug|release] [--abi arm64-v8a|x86_64] [--features "..."]` builds
|
||||
the cdylib and the APK and verifies it (badging, and signing for a
|
||||
release build); `run-bench.sh [--apk PATH]` installs on this
|
||||
checkout's own emulator (`emu serial`), taps "Run benchmark" by
|
||||
label (no coordinates), polls logcat for the report line, and prints
|
||||
it. Used for everything below and for the redelivery at the end of
|
||||
this box.
|
||||
|
||||
**Numbers, this checkout's AVD (`ai-app-2`), release, x86_64,
|
||||
`force-gles`, via `run-bench.sh` -- three separate runs, same warm
|
||||
AVD (not a fresh cold boot each time):**
|
||||
|
||||
frames=690 janky%=78.26 p50=26.9ms p90=60.3ms p99=103.4ms worst=130.1ms cpu_p50=7.4ms gpu_wait_p50=15.7ms
|
||||
frames=691 janky%=84.95 p50=28.3ms p90=60.9ms p99=95.2ms worst=120.7ms cpu_p50=5.4ms gpu_wait_p50=18.2ms
|
||||
frames=691 janky%=58.32 p50=18.9ms p90=40.3ms p99=75.6ms worst=101.4ms cpu_p50=3.5ms gpu_wait_p50=12.5ms
|
||||
|
||||
Against this same box's earlier iris-half reading (full rebuild per
|
||||
event, a *different*, freshly-booted x86_64 AVD, host GPU):
|
||||
`frames=372 janky%=56.99 p50=19.5ms p90=219.5ms p99=284.5ms
|
||||
worst=369.3ms cpu_p50=0.4ms gpu_wait_p50=13.9ms`. The tail is what
|
||||
moved: `worst` dropped from 369.3ms to 101–130ms and `p99` from
|
||||
284.5ms to 76–103ms across all three post-fix runs, consistent with
|
||||
removing the periodic full-tree-rebuild stall during the
|
||||
20-events/second streaming phase. `p50`/`cpu_p50` are *not* a clean
|
||||
comparison -- these three runs share one already-warm AVD instance
|
||||
rather than each getting its own fresh cold boot the way the earlier
|
||||
reading did, and `cpu_p50` in particular is noisy run to run (3.5 to
|
||||
7.4ms here) in a way a controlled A/B would need to separate from
|
||||
the code change itself. **What a future pass should do for a clean
|
||||
number**: two fresh cold boots of the same AVD, one per build,
|
||||
`run-bench.sh` on each, nothing else running.
|
||||
|
||||
**The three remaining I5 verifications the dropout-root-cause pass
|
||||
left open (this box's "Touch-scroll dropout root-caused" subsection)
|
||||
-- attempted this pass, blocked, not silently dropped.**
|
||||
`app/iris-scroll.sh` needs `dev.iris.android.demo`'s plain
|
||||
`transcript-screen` (non-`bench`) debug build, which needs a live
|
||||
sandbox server (`app/ui-sandbox.sh`) to bake in at build time
|
||||
(`build.rs`'s `AI_APP_TRANSCRIPT_HOST`/etc, skipped only under
|
||||
`bench`). `./ui-sandbox.sh start` fails to build **the Rust
|
||||
`ai-server` itself**, unrelated to anything in this box's diff:
|
||||
`error[E0599]: no variant named 'LimitReached' found for enum
|
||||
'event_model::Event'` in `server/src/session/mod.rs:2633`,
|
||||
`server/src/session/claude/translate.rs:307`, and
|
||||
`server/src/session/echo.rs:389` -- `event_model` and `server` have
|
||||
drifted out of sync on this branch, most likely from concurrent
|
||||
work elsewhere on `rustify` (this pass touched nothing under
|
||||
`server/` or `event-model/`, confirmed by `git status`). Since
|
||||
fixing that is a separate, unrelated repair (and risks colliding
|
||||
with whatever pass is mid-edit there), this pass did not attempt
|
||||
it. Consequently, not done this pass: the three `iris-scroll.sh`
|
||||
runs, the host-GPU `FrameReport` table row retake, and the
|
||||
`EMU_GPU=software` + `force-gles` cold-boot `FrameReport` (the
|
||||
backend-isolation question) -- none of the three need the broken
|
||||
server directly, but the first two need the same debug build the
|
||||
server outage blocks, and by the time that was found there was not
|
||||
enough of this pass left to justify a fresh `EMU_GPU=software` cold
|
||||
boot (several minutes) for the third alone without also covering the
|
||||
other two on the same session. A future pass: fix or wait out the
|
||||
`server`/`event_model` drift, rebuild `dev.iris.android.demo` with
|
||||
plain `transcript-screen` via `./build-apk.sh debug --abi x86_64
|
||||
--features "transcript-screen force-gles"`, then run
|
||||
`app/iris-scroll.sh` three times and retake the host-GPU row, and
|
||||
separately cold-boot with `EMU_GPU=software` for the third.
|
||||
|
||||
**Redelivered, 2026-09-05.** `./build-apk.sh release --abi
|
||||
arm64-v8a` (arm64-only jniLibs; an earlier step in this same pass
|
||||
had left an x86_64 slice in there from the emulator testing above,
|
||||
removed before this build so the delivered APK matches P0's
|
||||
original arm64-only shape) -- `aapt2 dump badging` confirms
|
||||
`native-code: 'arm64-v8a'` and the same
|
||||
`dev.iris.android.demo.bench` id, `apksigner verify` the same
|
||||
`CN=ai-app` cert as before. Copied over
|
||||
`~/host/bench/iris-bench-arm64.apk`; `~/host/bench/README.md` gained
|
||||
a one-line build-date/commit note so Iris can tell which build she
|
||||
has.
|
||||
|
||||
- [ ] **P1 — session screen parity.** History paging backward (with the
|
||||
page-boundary healing `client-core` does not have yet, below),
|
||||
`TranscriptSource`-backed cache/server stitching, jump-to-latest,
|
||||
|
||||
Reference in new issue
Block a user