diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index dc8cda9..d02118e 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -7,6 +7,28 @@ marked **DEFERRED** are ones the agent chose not to decide alone. ## 2026-09-05 +- **The rest of the port is one UI crate, `iris/app-ui`, grown out of + `iris/transcript-ui` rather than started beside it.** It holds a + `Screen` enum plus a back stack — the Rust equivalent of `AppRoot.kt`'s + `when` — and `iris/desktop-app`/`iris/android-app` become thin entry + points over it. Chosen over a fresh crate because `transcript-ui` + already has the right generic shape (`Rsc: HasEvents` + + `Rsc::State: FocusHost`) and the `client-core`/`event-model` path + dependencies every later screen needs, so growing it in place is the + smaller diff. Platform-only code (notification service, share target, + QR scanner, Keystore token, deep-link enrolment) stays in the E3/E5 + Java shell (`android-shell/` + `app/shellApp`) rather than moving into + this crate, since none of it is a screen. The Android APK is built by + `cargo xtask apk` (E5), merging the app-ui cdylib into the E3 shell so + there is one app rather than a demo shell plus a service shell. + `app/androidApp` (the Compose app) stays untouched and is the baseline + every step is measured against, until parity is reached (P7 decides + the switch, and is itself a load-bearing decision left to Iris). Order + is by risk to the daily-use path: session screen first (P1, where + every hard behaviour already lives), then the shell merge and a real + phone install (P2), then root tabs (P3), the explorer (P4), + settings/enrolment (P5), desktop parity (P6), and the cutover itself + (P7). Full plan: RUST.md's "The port, in order (decided 2026-09-05)". - **iris gets its own measured frame report, rather than waiting on a `dumpsys`/`gfxinfo` answer that cannot see a `SurfaceView`'s GPU-drawn frames.** `iris_core::FrameReport` (`iris/core/src/render/frame_report.rs`) diff --git a/docs/IRIS_TODO.md b/docs/IRIS_TODO.md index 2cb008e..a773bdf 100644 --- a/docs/IRIS_TODO.md +++ b/docs/IRIS_TODO.md @@ -292,6 +292,42 @@ order and what "done" looks like. Tick and date them in place. everything, the same way input is**. Whatever the mechanism, a widget that does not animate must pay nothing and import nothing for it. +## Build (for the port) + +Widgets `RUST.md`'s "The port, in order (decided 2026-09-05)" needs and +iris does not have yet, one entry per gap, named against the P-step that +first needs it. Move an entry up to "Fix" or tick it in place once built; +do not duplicate it there. + +- [ ] **A history-paging cushion measured in on-screen viewports, not a + row count.** (**P1**.) `iris::widget::List` has no equivalent of the + Compose app's `HISTORY_SCREENS` — AGENTS.md's "Things that have + bitten" is explicit that a fixed row count under-fills a screen on a + tool-heavy transcript and over-fills one on a text-heavy one, so + whatever loads the next page has to ask the list how many viewports + are actually on screen, not assume a constant. +- [ ] **A scaled thumbnail/image widget for an in-transcript image.** + (**P1**.) `SessionImage.kt`'s bitmap decode-and-downscale has no iris + counterpart; iris's own image widget (used by `bench_images.rs`) draws + a loaded texture but does nothing about sourcing or scaling one from a + server-produced attachment. +- [ ] **A modal/dialog primitive.** (**P1**, reused by **P3** and + **P5**.) Needed for the session settings dialog, `UsageDialog`'s + equivalent, and the delete-with-`deleteForeign` confirmation with its + toggle switch. Build once, wherever it is first needed, rather than + once per screen that wants one. +- [ ] **A horizontal gauge/bar widget.** (**P1**.) For + `SessionUsageBar`'s equivalent — a bounded fill reflecting a fraction, + nothing fancier. +- [ ] **A `BusyItem` equivalent: a dimmed row carrying an operation + label that does not block its list's own scroll/drag.** (**P3**.) The + Compose version tried an overlay first and it swallowed the drag along + with the tap (AGENTS.md's "Shared appearance") — worth not repeating + that attempt in iris before building the row-level version directly. +- [ ] **A toggle switch.** (**P3**.) For the delete dialog's + `deleteForeign` control; iris has no switch/checkbox widget yet as far + as this pass found. + ## Reconsider - [ ] **`WidgetView`.** Iris is unsure of it: what she wants is an easy way diff --git a/docs/RUST.md b/docs/RUST.md index b8fb09d..05e07f8 100644 --- a/docs/RUST.md +++ b/docs/RUST.md @@ -40,6 +40,12 @@ session spending an afternoon on them again. numbers in I5's box and E1/E2's findings. See the Recommendation's item 3 and `DECISIONS.md`. Next: the remaining screens and the app on iris — a new ordered list is the next thing to write into this file. +- **The port plan exists, 2026-09-05: "## The port, in order (decided + 2026-09-05)"**, seven steps (P1–P7) below "Experiments, in order," + ordered by risk to the daily-use path rather than by screen count. **P1 + — session screen parity — is next.** One crate decision made there: + screens grow out of `iris/transcript-ui` into `iris/app-ui`, with + `desktop-app`/`android-app` as thin entry points over it. - **I5 is now `[x]`: a clean, single-session, like-for-like 24-swipe scroll comparison between Compose and iris exists, 2026-09-05.** Same sandbox session content for both apps, same emulator, `EMU_GPU=software` @@ -3195,6 +3201,236 @@ silently on real hardware. `e2a1fad`'s own message. `docs/DECISIONS.md`'s DEFERRED item is updated with this section's host-GPU table below. +## The port, in order (decided 2026-09-05) + +Iris decided iris over Masonry (`DECISIONS.md`). This is the ordered plan +for the rest of the app, decided by the design agent per the standing +"decide technical questions yourself" instruction — a serious +user-facing tradeoff is not in play in the ordering itself, so it is not +deferred to her. **Crate shape, decided here**: the screens live in one +crate, **`iris/app-ui`**, grown from `iris/transcript-ui` rather than +started beside it — `transcript-ui` already has the right generic shape +(`Rsc: HasEvents` + `Rsc::State: FocusHost`, the same axis `tabs-ui` +varies along) and the same `client-core`/`event-model` path +dependencies every later screen needs, so growing it in place is a +rename plus new modules rather than a second crate re-declaring +dependencies the first already has. It holds a `Screen` enum and a back +stack — the direct equivalent of `AppRoot.kt`'s `when` and `MainScreen.kt`'s +tab `enum` — with each Compose screen becoming one `iris::widget` +subtree module. `iris/desktop-app` (E4) and `iris/android-app` (I2/I5) +become thin entry points that call into `app-ui`, the way `AppRoot`/ +`MainActivity` today call into Compose screens they don't otherwise own. +Platform-only code (the notification foreground service, the share +target, the QR scanner, the Keystore-sealed token, deep-link enrolment) +stays exactly where E3/E5 already put it — `android-shell/` and +`app/shellApp` — since none of it is a screen `app-ui` could draw. + +Order is by **risk to the daily-use path**, not by screen count: the +session screen is what the app is for and where every hard behaviour +(paging, cache, keyboard insets, selection) already lives, so it goes +first and on the phone as reachable code as soon as possible, before the +lower-risk screens. + +Every step below assumes the `app/ui-sandbox.sh` fixtures (AGENTS.md's +"The rigs") and the `this-machine-android` skill's facts (per-checkout +AVD, `ui-trace` by accessibility name, GrapheneOS phone quirks, the +`adb shell` quoting traps) apply unchanged — read that skill before +running any pass condition below that touches an emulator or a real +device. + +- [ ] **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, + tool-call cards and grouping, the session settings dialog, composer + attachments, and the keyboard/insets behaviours AGENTS.md's "Things + that have bitten" names (the floating-composer bug, `adjustResize`, + the `imePadding`-vs-raw-inset rule). This is the highest-risk step: + it is the screen the app is used for, every hour of the day. + + **Kotlin it replaces**: `SessionScreen.kt`, `TranscriptList.kt`, + `SessionSettingsDialog.kt`, `ToolInput.kt`, `ToolRows.kt`, + `AskQuestion.kt`, `Compaction.kt`, `SessionUsageBar.kt`, + `PendingAttachments.kt`, `Attachment.kt`, `Attachments.kt`, + `SessionImage.kt`, `MemoryNote.kt`, `PeerMessage.kt`, `RawBlock.kt`, + `CodeFence.kt`, `MarkdownLinks.kt`, `MarkdownPieces.kt`, + `Markdown.kt`, `Bubble.kt`, `ScrollAnchor.kt`, `Drafts.kt`, + `UsageDialog.kt`, `Chevron.kt`, `Dividers.kt`. (`transcript-ui` + already covers the row/markdown/selection/composer core these sit + on top of or beside.) + + **`client-core` needed, and what is not yet covered and must be + ported first** (`CLIENT_CORE.md`): `TranscriptSource.kt` (deciding + cache vs. server per page and stitching them — "not started"), + `TranscriptItems.kt`'s `joinPages`/`healSplitMessage`/`adoptRun` + (page-boundary healing — "not ported," and paging backward is + exactly what exercises it), the markdown *block* model beyond + syntax spans (headings/lists/tables/fences as distinct nodes — + "not started," needed for `CodeFence`/`MarkdownPieces`' equivalents), + and the attachments route (`/sessions/{id}/attachments` — "not + covered" in `api.rs`, needed for `PendingAttachments`/`Attachment`). + + **iris widgets missing, → `IRIS_TODO.md`'s new "Build (for the + port)" section**: row-level accessibility names and the tappable + link / background-chip primitive (both already listed under I5's + leftovers — this step is what needs them, not a new ask); a + history-paging cushion measured in on-screen viewports rather than + a row count (the `HISTORY_SCREENS` lesson in "Things that have + bitten," which iris's `List` has no equivalent of yet); a scaled + thumbnail/image widget for `SessionImage`'s in-transcript images; a + modal/dialog primitive for the session settings dialog and + `UsageDialog` (iris has none today — check before building a second + one for P3/P5); a horizontal gauge/bar widget for + `SessionUsageBar`. + + **Pass condition**: `app/ui-sandbox.sh`'s fixtures driven by + `ui-trace record --do "tap '