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:
irisandClaude Fable 5.1 committed 2026-09-05 22:14:35 -04:00
1 parent 5655fa8093
commit 46d3a6fd41
3 files changed
+199 -16

No files matched your search

+47
View File
@@ -437,3 +437,50 @@ with a number instead of a guess (RUST.md's I5 box).
blocked handing the frame to the driver," not a confirmed GPU-completion
time. Enough to separate "iris is slow building the frame" from "iris is
slow handing it off," not enough to claim an exact GPU budget.
## 2026-09-05: `List::replace_back`/`List::clear`, and `TranscriptScreen::apply`
Fixes the "every client refolds and rebuilds the whole widget tree per
streamed event" cost RUST.md's P0 box measured (20 events/second against a
~3,200-row transcript). Two small additions to `iris::widget::List`
(`iris/src/widget/list.rs`), plus one new method on `transcript-ui`'s
`TranscriptScreen`.
- **`List::replace_back(row: ListRow) -> Option<ListRow>`**: swaps the
*last* row's widget for a new one without moving it — same slot index,
so an anchor already pinned there (in particular a list flush with its
own end) stays pinned, and a `List` scrolled elsewhere is untouched.
`None` if the list is empty. `RowKey` may differ between the old and new
row; only `heights`/`extents` care, and both are invalidated for the
evicted key the same way `pop_back` already does.
- **`List::clear()`**: drops every loaded row and resets to `List::new`'s
state (`more_before`/`more_after` untouched — a caller that wants those
cleared too calls `set_more_before(None)`/`set_more_after(None)` itself).
The fallback path for a change that touches more than the tail.
- **`transcript_ui::TranscriptScreen::apply(&self, rsc, old: &[TranscriptItem], new: &[TranscriptItem])`**:
the incremental alternative to rebuilding the whole screen from
`transcript_ui::build_tree` on every folded event. Diffs the two
`group_tool_runs` outputs and picks the cheapest update: nothing changed
(no-op), a pure append (`push_row`, unchanged cost), or — the common
streaming case, a delta into a still-open assistant message — a rebuild
of just the one changed row via `List::replace_back`, with any further
new rows appended after it. A row changing *before* the tail (only
`group_tool_runs` retroactively grouping tool calls into a run does
this) falls back to `List::clear` plus a full rebuild, counted in
`TranscriptScreen::take_rebuilds()`. `bench_client.rs`, `transcript_client.rs`
and `desktop-app/app.rs` all call this now instead of rebuilding on every
event; only the opening page (and `apply`'s own fallback) still calls
`build_tree`.
- **`TextEditCtx::set_with_spans(text, spans)`**: `set()` plus a fresh
`Vec<SpanStyle>` in one call, needed because a streamed row's markdown
re-renders to both a new string and a new span list on every delta and
the two have to land together — a stale span list drawn against new
text can point past its end. `set()` itself is unchanged (still clears
spans to none, as before).
Measured on this checkout's emulator (`iris/android-app/run-bench.sh`,
release, x86_64, `force-gles`): worst-frame and p99 during the streaming
phase dropped from 369.3ms/284.5ms (full rebuild per event, prior pass) to
~101130ms/~76103ms across three runs (this fix) — see RUST.md's P0 box
for the full numbers and the comparison's caveats (different AVD
instances, not a controlled A/B on identical hardware state).