Prune commentary and stale Rust port notes
This commit is contained in:
1 parent
5428cd75c9
commit
25370731d0
193 files changed
+693
-16219
No files matched your search
+47
-334
@@ -1,50 +1,18 @@
|
||||
# Moving the app to Rust
|
||||
|
||||
Working document for the port Iris asked for on 2026-09-04: the phone app
|
||||
in pure Rust, one UI framework shared with a desktop app, at full feature
|
||||
parity and giving up nothing native -- performance especially. Her
|
||||
constraints: no Dioxus and nothing that draws through a WebView; **no UI
|
||||
DSL** (which ruled out Makepad and Slint); the result stays lightweight;
|
||||
platform-specific pieces are fine to maintain; reimplementing a framework
|
||||
piece from scratch where it does not fit is fine; effort and elapsed time
|
||||
do not matter, long-term robustness does.
|
||||
|
||||
**The framework question is closed.** Iris chose her own library,
|
||||
[iris](https://github.com/cat16/iris), over Masonry on 2026-09-05.
|
||||
The bake-off that got there, and the twelve experiments
|
||||
that proved it on a device, are summarised in "What the experiments
|
||||
settled" below rather than kept at length. What is left in this file is
|
||||
the plan for the rest of the app and the findings that outlive the tasks
|
||||
that produced them.
|
||||
|
||||
Decisions get a date and a reason here, the way `PLAN.md` does.
|
||||
Plan for a native Rust phone app with full feature parity and a shared desktop
|
||||
UI. It uses [iris](https://github.com/cat16/iris); platform-specific entry
|
||||
points are acceptable, but shared screens, widgets, and styling are not
|
||||
duplicated. The result must stay lightweight and preserve native behavior and
|
||||
performance.
|
||||
|
||||
## Keep this file current as you work
|
||||
|
||||
**This file is the handoff, and it is meant to let a session be cleared.**
|
||||
Write each result into it *as you get it*, not at the end: the box ticked
|
||||
or the reason it could not be, the measurement with its number, the
|
||||
decision with its date and what it rejected, and anything that cost time to
|
||||
find out. Then a session that has filled its context can be cleared and the
|
||||
next one can pick up from this file alone, which is much cheaper than
|
||||
carrying a long conversation or re-deriving what was already measured.
|
||||
Keep open work, current design, measured constraints, and dead ends that would
|
||||
otherwise be repeated. Delete completed plans and migration narratives. Name
|
||||
the command and measured value when evidence matters.
|
||||
|
||||
Two things that follow. Write for somebody who was not here -- name the
|
||||
command, the file and the number rather than "the fix" or "the earlier
|
||||
run". And write the failures and the dead ends too: "Venus is blocked by
|
||||
the emulator, not by Mesa" and "the present mode was not the cause" are
|
||||
worth as much as the successes, because they are what stops the next
|
||||
session spending an afternoon on them again.
|
||||
|
||||
**And delete a plan once it has been carried out** (Iris, 2026-09-08:
|
||||
*"remove everything that's already done and decided... many with checkboxes
|
||||
already ticked off that just fill up context"*). A ticked box has done its
|
||||
job; a finished experiment is worth one line saying what it settled, not
|
||||
the log of settling it. Currency means this file says where things *are*,
|
||||
not how they got here. What survives a prune is what cannot be cheaply
|
||||
re-derived: measurements, dead ends, invariants and their reasons.
|
||||
|
||||
## Where things stand (2026-09-09)
|
||||
## Current status
|
||||
|
||||
- **The framework is decided and built on.** iris draws the transcript
|
||||
screen on the desktop, on this checkout's emulator and on Iris's phone.
|
||||
@@ -52,14 +20,12 @@ re-derived: measurements, dead ends, invariants and their reasons.
|
||||
phone and the reports are under `docs/bench/`.
|
||||
- **P1 (session screen parity) is the current work**, and is where the
|
||||
next session should start. Its box below has the state.
|
||||
- **The repository was reorganised on 2026-09-08**: the port is one crate,
|
||||
`app-rust/`, and `iris/` is the UI framework alone. See "One app crate"
|
||||
at the end -- it is the layout everything else here assumes.
|
||||
- The port is one crate under `app-rust/`; `iris/` is only the UI framework.
|
||||
- **Open across the rest of the docs**: `docs/IRIS_TODO.md` is iris's own
|
||||
list (colour-space correctness is the live one), `docs/TODO.md` is the
|
||||
Compose app's.
|
||||
|
||||
## Desktop and phone share the code (Iris, 2026-09-07)
|
||||
## Desktop and phone share the code
|
||||
|
||||
Iris plans to develop a desktop app as well, and asked that most code be
|
||||
sharable between desktop and phone. The tree already has that
|
||||
@@ -332,76 +298,9 @@ rather than what it happens to look like:
|
||||
7. **Measurable frames**: the debug render report, and a way to attribute
|
||||
a frame's cost to a widget on the real phone.
|
||||
|
||||
## What the experiments settled
|
||||
## Measurements and constraints
|
||||
|
||||
Twelve boxes, all closed between 2026-09-04 and 2026-09-05, and all
|
||||
deleted on 2026-09-08 now that their conclusions live in the code. One
|
||||
line each for what a later session must not re-derive; where a decision
|
||||
needs its reasoning, the reasoning is at the thing itself.
|
||||
|
||||
**The framework track (E0-E5), against Masonry:**
|
||||
|
||||
- **E0 -- toolchain.** NDK r29 (`29.0.14206865`) under `~/Android/Sdk`,
|
||||
cargo-ndk 4.x. Its API-level flag is `-P`; `-p` now means `--package`.
|
||||
- **E1 -- android-view's Masonry demo ran here**, on the GPU, with an
|
||||
accessibility tree and the phone's real keyboard -- but no autocorrect
|
||||
and no suggestions. The `android-view` rev this was measured against is
|
||||
pinned in `app-rust/Cargo.toml` with that history at the pin;
|
||||
`accesskit_android`'s detach-abort is mitigated in
|
||||
`iris/src/android/view.rs`'s `raise_if_enabled`, and advancing the
|
||||
version is not the fix.
|
||||
- **E2 -- a transcript in Masonry** found the framework-wide gap that
|
||||
blocked the comparison. It lived in `~/src/android-view/e2-transcript`
|
||||
and was never committed here.
|
||||
- **E3/E5 -- the Kotlin shell and the packaging xtask.** Both hold:
|
||||
`app/shellApp` plus the JNI bridge (now `app-rust`'s `shell` feature)
|
||||
posts a real notification and receives a real share, and `cargo xtask
|
||||
apk` packages an installable APK with `javac`/`d8`/`aapt2`/`zipalign`/
|
||||
`apksigner` and one disclosed Gradle call, documented at
|
||||
`scripts/xtask/src/apk.rs`'s module doc.
|
||||
- **E4 -- the same screen on the desktop**, which is now
|
||||
`app-rust`'s `src/desktop` and the `ai-app-desktop` binary.
|
||||
|
||||
**The iris track (I0-I5):**
|
||||
|
||||
- **I0a -- iris is vendored at `iris/`**, history not carried, consumed by
|
||||
path, from `iris/iris` on gitea at `7b54aaf`. It goes back to its own
|
||||
repository once it has proved itself.
|
||||
- **I0b -- the nightly pin is dated, not rolling** (`rust-toolchain.toml`,
|
||||
one copy in `iris/` and one in `app-rust/`, because a pin applies per
|
||||
directory). Dated because a rolling channel moved `impl const Trait` to
|
||||
`const impl Trait` underneath the vendored tree and broke it unattended.
|
||||
- **I1 -- parley, plus a glyph atlas.** Both Iris's call. Parley addresses
|
||||
text by byte offset into one string, which is why the editing model
|
||||
looks the way it does.
|
||||
- **I2 -- iris runs on android-view**: the backend, the Gradle shell,
|
||||
insets, the back gesture and the full `InputConnection` bridge, with
|
||||
real Gboard suggestions.
|
||||
- **I3 -- the virtualised list.** Since renamed `LazySpan`, and scrolling
|
||||
has moved out of it into `ScrollController` -- `docs/SCROLL.md` is the
|
||||
current design, not this box.
|
||||
- **I4 -- accessibility names through AccessKit**, one flat tree with a
|
||||
synthetic `Role::Window` root and every *named* widget a direct child.
|
||||
Flat deliberately: nothing upstream of a named leaf needs a node. This
|
||||
is what lets `ui-trace` tap by label.
|
||||
- **I5 -- the transcript screen in iris**, with `FrameReport` for
|
||||
frame timing. Its descendants are `app-rust/src/ui` and every
|
||||
measurement rig in AGENTS.md.
|
||||
|
||||
**Two findings from that period that are still load-bearing, kept where
|
||||
they belong rather than here:** iris's binding array does not survive real
|
||||
Android hardware (the measurement and the fix are `docs/TEXTURES.md`'s
|
||||
"Implemented, 2026-09-04"), and the emulator has no hardware Vulkan while
|
||||
its GLES *is* the host's real GPU through virgl (moved to the
|
||||
`this-machine-android` skill on 2026-09-08, with the `gpu-probe` output
|
||||
that established it).
|
||||
|
||||
## Findings that outlive the task that produced them
|
||||
|
||||
Kept because the number or the constraint is what stops it being
|
||||
re-derived; the tasks themselves are done and deleted.
|
||||
|
||||
### The Android release profile, and where the APK's size went (2026-09-07)
|
||||
### The Android release profile and APK size
|
||||
|
||||
Iris asked why the iris bench APK was double the Compose one (20.6 MB vs
|
||||
10.1 MB). It was almost all `libmain.so`, built with `panic = "abort"` and
|
||||
@@ -423,7 +322,7 @@ vectorisation on a renderer. Everything else is
|
||||
own rather than `release`, so the desktop build is not also optimised for
|
||||
size.
|
||||
|
||||
### The fling stutter, and what a frame report could not say (2026-09-09)
|
||||
### The fling stutter and what a frame report cannot say
|
||||
|
||||
Iris, from her phone: *"I'm noticing some stuttering when flinging in
|
||||
particular. Harder to notice with my finger directly moving the scroll."*
|
||||
@@ -515,7 +414,7 @@ The signature of the fixed loop, from that run: `build p50 0.4ms,
|
||||
acquire p50 5.7ms, submit p50 1.7ms` -- four tenths of a millisecond of
|
||||
work and the rest of the refresh period spent waiting its turn.
|
||||
|
||||
### Streaming is where the frame time is now (2026-09-09)
|
||||
### Streaming frame time
|
||||
|
||||
Measured after the fling was fixed, and it is not where it looks.
|
||||
`frame_profile.rs`'s stream run: folding an arriving event is 0.35ms and
|
||||
@@ -530,7 +429,7 @@ every delta.
|
||||
exact shape of the Compose lesson in AGENTS.md's "Things that have
|
||||
bitten" -- and measuring it is what ruled it out.
|
||||
|
||||
### Incremental text: parley cannot, and it turns out not to matter (2026-09-09)
|
||||
### Incremental text shaping
|
||||
|
||||
Iris asked to investigate incremental text rendering and hoped parley
|
||||
supported it. **It does not, by design.** The crate's own docs: a
|
||||
@@ -635,7 +534,7 @@ the reply into blocks was still right -- it is what makes the fixture
|
||||
representative, and it halved the CPU half -- but it was never going to
|
||||
move this, and it slightly increases the primitive count.
|
||||
|
||||
### The arenas upload deltas, and stopped being 11x too big (2026-09-09)
|
||||
### Arena delta uploads
|
||||
|
||||
Done, and measured by `scripts/rigs/ui-profile`'s `arena_churn` -- see
|
||||
AGENTS.md's entry for the rig and the numbers. The arithmetic above was
|
||||
@@ -697,7 +596,7 @@ instance bytes per frame are **1,488**, from 176,496. This is framework
|
||||
layout/rendering behaviour and the transcript screen contains no special
|
||||
case for it.
|
||||
|
||||
### The Android release profile is `opt-level = 3`, not `"s"` (2026-09-09)
|
||||
### The Android release profile uses `opt-level = 3`
|
||||
|
||||
The table above was measured in bytes only. `"s"` costs the loop
|
||||
vectorisation and inlining a renderer runs on: over the same warm fling
|
||||
@@ -708,7 +607,7 @@ refused for `"z"`, one level further up. Iris raised it herself
|
||||
(*"I'd make sure it's in release mode"*); the build always was, and this
|
||||
was the part of "release" that was not about speed.
|
||||
|
||||
### Platform fonts, not bundled ones (2026-09-07)
|
||||
### Platform fonts
|
||||
|
||||
Iris: *"remove the font for now; just match what compose does."* The
|
||||
Compose app takes body text from `FontFamily.Default` and code from
|
||||
@@ -733,26 +632,9 @@ desktop cannot answer it. Before the next phone build, look at a bold run
|
||||
and at `CLOSED_MARK`/`OPEN_MARK`/`UP_MARK` (U+25B8/BE/B4) on Iris's own
|
||||
device; the emulator's font set is not evidence for hers.
|
||||
|
||||
### Hit-testing does not consult the mask chain (review R2, 2026-09-07)
|
||||
## The port, in order
|
||||
|
||||
Masks are applied in the fragment shader
|
||||
(`iris/core/src/render/shader.wgsl`); the CPU hit path
|
||||
(`UiRenderState::resolved_region`) does not look at `masks` at all. So a
|
||||
straddling row's clipped-away top is invisible and still tappable -- a tap
|
||||
on "Run benchmark" can land on an invisible link in the row behind it.
|
||||
Left deliberately: `docs/LAYOUT.md`'s mask redesign ("masks reference a
|
||||
drawn primitive instead of copying a shape") is where hit-testing gets the
|
||||
shape, and intersecting a chain in `resolved_region` now would be a second
|
||||
mechanism to unpick.
|
||||
|
||||
## The port, in order (decided 2026-09-05)
|
||||
|
||||
The ordered plan for the rest of the app, decided here per Iris's standing
|
||||
"decide technical questions yourself" instruction -- no serious
|
||||
user-facing tradeoff is in play in the ordering itself.
|
||||
|
||||
**Where the screens live** was settled by the 2026-09-08 reorganisation
|
||||
("One app crate", below): every screen is a module under
|
||||
Every screen is a module under
|
||||
`app-rust/src/ui`, which 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.
|
||||
@@ -776,55 +658,15 @@ AVD, `ui-trace` by accessibility name, GrapheneOS phone quirks, the
|
||||
running any pass condition below that touches an emulator or a real
|
||||
device.
|
||||
|
||||
- [x] **P0 -- the phone benchmark gate. Passed.** Asked for 2026-09-05,
|
||||
delivered and run on Iris's own phone; the reports are under
|
||||
`docs/bench/`. Both halves are still in the tree and are how a
|
||||
frame-time comparison is taken: the Compose `bench` build type
|
||||
(`app/`, `BenchFixture.kt`/`BenchRun.kt`) and the Rust `bench`
|
||||
feature (`app-rust`, `src/android/bench_client.rs`), opening the
|
||||
same checked-in synthetic transcript
|
||||
(`app/bench-fixture/assets/transcript.jsonl`, never a real one) with
|
||||
no server, driving the same scroll loop and streaming phase, and
|
||||
printing the same report fields. AGENTS.md's "The rigs" is the
|
||||
current description; `app-rust/build-apk.sh` and `run-bench.sh` are
|
||||
how it is run. The build source is this repository, but the Dev Updater
|
||||
publication is the separate `~/repos/ai-app-bench` repository, whose
|
||||
`iris-bench` component serves a committed APK with no build step. After
|
||||
an arm64 release build, replace its
|
||||
`iris/build/outputs/apk/release/iris-bench-arm64.apk` and push that
|
||||
repository; adding the component here or pushing only `ai-app-2` is the
|
||||
wrong delivery path.
|
||||
**Bench-only cleanup still open**: the diagnostics report pane draws over
|
||||
transcript rows. `REPORT_MAX_HEIGHT_DP` constrains its claimed height, but the
|
||||
pane is neither masked nor scrollable despite its construction comment saying
|
||||
it is both. This is an `app-rust` defect, not an iris framework item.
|
||||
|
||||
**Bench-only cleanup still open**: the diagnostics report pane draws
|
||||
over transcript rows. Reproduced on the emulator on 2026-09-09 by
|
||||
opening the named `Diagnostics` control. `REPORT_MAX_HEIGHT_DP`
|
||||
constrains its claimed height, but the pane is neither masked nor
|
||||
scrollable despite its construction comment saying it is both. This is
|
||||
an `app-rust` bench-screen defect, not an iris framework item.
|
||||
|
||||
- [ ] **P1 — session screen parity.** **Started 2026-09-06, on Iris's
|
||||
word**: "just continue with the plan for now; try to move towards
|
||||
feature parity for the transcript screen so that the test can be
|
||||
more fair." So P0's "must pass before P1 starts" is lifted — the
|
||||
phone bench continues alongside, and parity is what makes its
|
||||
comparison fair. **Sub-order, by what the bench fixture exercises
|
||||
and Compose already draws** (tick and date each in place):
|
||||
- [x] **P1a — markdown block rendering parity.** Done 2026-09-06.
|
||||
Each top-level block is drawn in one of three frames
|
||||
(`ui::markdown::BlockFrame`) — plain, verbatim, quote — with
|
||||
fences and tables verbatim, headings scaled, and inline
|
||||
styling per span. `app-rust/src/ui/markdown.rs` is the code
|
||||
and its module doc the design.
|
||||
- [x] **P1b — tool-call cards and grouping.** Done 2026-09-06.
|
||||
`ToolRows.kt`/`ToolInput.kt` ported to
|
||||
`app-rust/src/ui/tool.rs`: a run of calls is one collapsible
|
||||
group, each card carries its state and summary, and the five
|
||||
`ToolState` values each have their own appearance.
|
||||
`tool.rs`'s module doc has what was chosen.
|
||||
- [ ] **P1 — session screen parity.** Continue in this order:
|
||||
- [ ] **Before the next parity slice — make iris's colour pipeline
|
||||
correct.** Raised by Iris on 2026-09-09 as something to settle
|
||||
sooner rather than later. Both backends currently prefer an
|
||||
sRGB surface while the shader returns palette/image bytes as
|
||||
correct.** Both backends currently prefer an sRGB surface while
|
||||
the shader returns palette/image bytes as
|
||||
linear values; `IRIS_TODO.md` has the measured mismatch and
|
||||
pass condition. Do this before judging or centralising the
|
||||
app's styling. It is correctness, not cosmetic polish.
|
||||
@@ -1038,169 +880,40 @@ device.
|
||||
once more against a real `ai-server` (not the sandbox) on a real
|
||||
phone, side by side with the Compose build until it holds.
|
||||
|
||||
## For the next session
|
||||
## One app crate
|
||||
|
||||
What to do when you pick this up, in order, so nothing here has to be
|
||||
re-derived. **The work is done inline, not handed to subagents** — Iris
|
||||
said so on 2026-09-08 ("I'm no longer using subagents for this. Please do
|
||||
the work yourself"), so read the code, make the change, run the tests and
|
||||
push, in the session that picked the task up.
|
||||
|
||||
1. Read this file, then `AGENTS.md` and `PLAN.md`. The rules there
|
||||
(measure, do not read; fix the rig before accepting its limits; the
|
||||
emulator is this checkout's own) all apply.
|
||||
2. Work on the **`rustify`** branch of this clone (`ai-app-2`), not on
|
||||
`main` and not in `ai-app`. Nothing on this branch is production until
|
||||
Iris says so. Commit and push as you go.
|
||||
3. The E- and I-steps (the framework decision) are done — iris won,
|
||||
decided 2026-09-05. **Fix iris's colour-space pipeline first**, as Iris
|
||||
requested on 2026-09-09; then take P1c, history paging and
|
||||
jump-to-latest. The client-side paging pieces it needs are already
|
||||
ported.
|
||||
4. Every step ends with its measurement written into this file beside the
|
||||
box, and the box ticked or the reason it could not be written in its
|
||||
place. A step that is blocked says by what, not "later". Write it as you
|
||||
go rather than at the end — see "Keep this file current as you work".
|
||||
5. Run the existing rigs rather than inventing new ones: `ui-sandbox.sh`
|
||||
for a server with fixtures, `transcript-bench.sh` for the scroll
|
||||
baseline, `ui-trace` for anything positional, `emu up` for the
|
||||
emulator, `iris/run-headless.sh EXAMPLE --shot PNG` for an iris
|
||||
example on this displayless machine, and `scripts/rigs/gpu-probe` to ask a
|
||||
device (this VM, the emulator, or a real phone over `adb push`) what
|
||||
`wgpu` features and limits it actually has before building anything on
|
||||
the assumption it does. The Vulkan section below says how to get a
|
||||
Vulkan path in the emulator when a `wgpu` backend needs one.
|
||||
6. **Bound anything heavy at the moment you start it.** An emulator or a
|
||||
long build gets a deadline — `timeout`, or a watchdog scoped to the pid
|
||||
you just started — rather than a plan to stop it later. Scope it to
|
||||
that pid: a watchdog written as `sleep N; emu down` fired into a later
|
||||
experiment here and made a working Vulkan build look like a crash. And
|
||||
stop the emulator when the work needing it is done rather than between
|
||||
tasks.
|
||||
7. Decisions belong here with a date and what was rejected, the way
|
||||
`PLAN.md` does it. Do not put design into commit messages alone.
|
||||
|
||||
## Things a Rust app changes elsewhere
|
||||
|
||||
- **`wg-app-link`'s `:link`** (pinned TLS, enrollment store, QR activity)
|
||||
is Kotlin shared with Dev Updater. The certificate code already exists on
|
||||
the Rust side of the submodule; the pinned-CA build step
|
||||
(`generatePinnedCert`) becomes a `build.rs` reading the same path. The QR
|
||||
scanner stays a Kotlin activity, since the camera is a platform feature.
|
||||
- **Tooling** becomes `cargo` for everything but packaging: `cargo test`,
|
||||
`clippy`, `fmt` cover the whole client, which is the motivation. Gradle
|
||||
remains for the APK, signing (`~/.config/ai-app/release.jks`) and Dev
|
||||
Updater's build modes; `build-apk.sh` would call `cargo ndk` first.
|
||||
- **The bench scripts** (`ui-trace` by accessibility label) keep working
|
||||
only if the framework exposes names through AccessKit on Android; that is
|
||||
part of E2's pass condition, not a nicety.
|
||||
- **Icons** stay Nerd Font glyphs from the committed subset; Parley/Fontique
|
||||
loads a font file directly, so `build-icon-font.sh` is unchanged.
|
||||
|
||||
|
||||
## One app crate, 2026-09-08 (the repository reorganised)
|
||||
|
||||
Iris, reading the tree: *"the organization of the rust rewrite is a mess
|
||||
right now… there shouldn't be anything related to the app inside of iris.
|
||||
Iris is supposed to be the UI framework alone."* Then, on the crate count:
|
||||
*"I'm confused why the app only code needs more than one crate though."*
|
||||
|
||||
### What it was
|
||||
|
||||
Nine cargo workspaces, each with its own `Cargo.lock` and `target/`, and
|
||||
the port's project code in five places — `iris/transcript-ui`,
|
||||
`iris/transcript-fixture`, `iris/desktop-app`, `iris/android-app` (all
|
||||
*inside* the framework), plus `client-core` and `android-shell` at the
|
||||
root. Two root markdown files sat outside
|
||||
`docs/`.
|
||||
|
||||
### What it is
|
||||
|
||||
**One crate, `ai-app`, in `app-rust/`.** Modules, not crates:
|
||||
|
||||
| was | is |
|
||||
|----------------------------------------|-----------------------------------|
|
||||
| `client-core` | `src/client` |
|
||||
| `iris/transcript-ui` | `src/ui` |
|
||||
| `iris/transcript-fixture` | `src/ui/fixture.rs` + `tests/`, `touch/` |
|
||||
| `iris/desktop-app` | `src/desktop` + `src/bin_desktop.rs` |
|
||||
| `iris/android-app` | `src/android` + `android-project/` |
|
||||
| `android-shell` | `src/shell` |
|
||||
The Rust client is one `ai-app` crate in `app-rust/`: platform-free code
|
||||
is under `src/client` and `src/ui`, while `src/desktop`, `src/android`, and
|
||||
`src/shell` contain the platform entry points. The fixture is behind its
|
||||
own feature so its 1.9 MB `include_str!` does not enter ordinary phone
|
||||
builds.
|
||||
|
||||
`iris/` now holds `core`, `macro`, the `iris` crate, `tabs-ui` and
|
||||
`rig-input` — framework only, with no mention of a session, a transcript,
|
||||
a setup or a server anywhere in it.
|
||||
|
||||
### Why one crate really is enough
|
||||
`src/client` must not depend on iris. Features select the crate's face:
|
||||
`screens` for UI builds, `shell` for the Compose shell bridge, and `bench`
|
||||
for the fixture. The Android faces both produce `libai_app.so`.
|
||||
|
||||
Each split had a stated reason at the time; on inspection only two
|
||||
survived, and one of those is not in `app-rust` at all.
|
||||
`event-model` remains separate because both the server and client depend
|
||||
on that wire contract. Iris remains a separate UI-framework workspace and
|
||||
must contain no product concepts.
|
||||
|
||||
- **`client-core` separate from the UI** was "pure logic with no framework
|
||||
dependency". That property is worth keeping and does not need a crate:
|
||||
`iris` is behind the `screens` feature and `src/client/` may not reach
|
||||
it. An invariant on a module instead of on a manifest, stated in
|
||||
docs/CLIENT_CORE.md.
|
||||
- **`transcript-fixture` separate from `transcript-ui`** was so the
|
||||
headless harness and a desktop window opened the same bytes. Both are
|
||||
now the same crate, so it is `src/ui/fixture.rs` behind a `fixture`
|
||||
feature (1.9 MB of `include_str!` must not reach a phone build) with the
|
||||
six harness suites in `tests/`.
|
||||
- **Two Android `.so` names**, `libmain.so` for the iris app and
|
||||
`libandroid_shell.so` for the Kotlin shell's JNI bridge, looked like the
|
||||
one hard constraint: a package produces exactly one library artifact.
|
||||
It dissolves because **P2 already plans to merge those two Android apps
|
||||
into one**. So both faces come out of one package as `libai_app.so`,
|
||||
picked apart by features (`--no-default-features --features shell` keeps
|
||||
wgpu, parley and iris out of the Compose app's APK), which is the
|
||||
direction of travel rather than a workaround. `xtask apk` and
|
||||
`app/shellApp`'s `System.loadLibrary` were updated to match.
|
||||
- **A desktop binary and an Android cdylib in one package** is not a
|
||||
problem: `iris` itself already target-gates winit against android-view
|
||||
in one manifest, and the same table does it here. `build-apk.sh` passes
|
||||
`--lib` so `cargo ndk` never tries to build the desktop binary.
|
||||
- **`event-model` stays a crate**, and is the one split that was never
|
||||
optional: `server/` depends on it too, so a crate is what makes the
|
||||
backend and the app agree by construction. Iris chose to leave it at the
|
||||
repo root rather than inside `app-rust/`, since it is the contract
|
||||
between the two rather than app code.
|
||||
### Build constraints
|
||||
|
||||
So: three workspaces where there were nine — `event-model`, `server`,
|
||||
`app-rust` — plus `iris` and `xtask`.
|
||||
|
||||
### Things that moved with it, worth knowing
|
||||
|
||||
- **The toolchain pin is per directory.** `app-rust/rust-toolchain.toml` is
|
||||
a copy of `iris/`'s, because `client-core` used to build on stable and
|
||||
now shares iris's dated nightly. Two consequences appeared immediately:
|
||||
two `needless_range_loop` warnings in the markdown highlighter (fixed),
|
||||
and four `AtomicBool::fetch_update` deprecations from inside `jni`
|
||||
0.22's `native_method!` macro. The last are not ours to migrate — the
|
||||
fix is a `jni` release — so `src/lib.rs` carries an `#[allow(deprecated)]`
|
||||
scoped to `mod shell` with that reason written at it.
|
||||
- **The rolling nightly setting is per directory.** The toolchain files in
|
||||
`app-rust`, `iris`, and `scripts/rigs/ui-profile` must stay synchronized.
|
||||
- **The Android release profile is `android-release`, not `release`.** The
|
||||
aggressive settings `iris/android-app` had (`panic = "abort"`,
|
||||
`opt-level = "s"`, fat LTO) would otherwise apply to the desktop build
|
||||
too, which is a testing surface. `build-apk.sh` passes
|
||||
`--profile android-release` / `--profile android-dev`.
|
||||
- **`iris/run-headless.sh` grew `--dir DIR`**, defaulting to `iris/`. The
|
||||
rig belongs to the framework; the examples it usually runs no longer do.
|
||||
`replay-touch` is still built from `iris/`.
|
||||
- **The log target changed** from `client_core` to `ai_app`
|
||||
(`src/client/log_ring.rs`'s `is_own_target`).
|
||||
- **`iris/run-headless.sh --dir DIR`** selects the workspace containing the
|
||||
example; it defaults to `iris/`.
|
||||
- **Not renamed, deliberately:** the Android application id and Java
|
||||
package are still `dev.iris.android.demo` and the label is still "iris
|
||||
android-view demo", both now misleading. Changing them changes the app's
|
||||
identity on Iris's phone (a side-by-side install rather than an upgrade)
|
||||
and the `DevLogProvider` authority Dev Updater reads, so it is hers to
|
||||
decide rather than a tidy-up to make quietly.
|
||||
|
||||
### Verified
|
||||
|
||||
`./scripts/run-tests.sh` (event-model, server, app-rust) and `cd iris && cargo
|
||||
test` green; `cargo clippy --all-targets` and `cargo fmt` clean in every
|
||||
workspace. `cargo ndk -t x86_64` links `libai_app.so`; `./build-apk.sh
|
||||
debug --abi x86_64` produces an installable APK; installed and launched on
|
||||
this checkout's emulator, drawing through `Gl … virgl` as expected. The
|
||||
phone-sized headless screenshot (`run-headless.sh phone --phone --dir
|
||||
../app-rust --shot …`) renders the transcript unchanged.
|
||||
and the `DevLogProvider` authority Dev Updater reads, so changing them
|
||||
requires an explicit migration decision.
|
||||
Reference in new issue
Block a user