RUST.md: bring it up to date, and say it must be kept that way

Adds a "Keep this file current as you work" section at the top saying what
it is for: this file is the handoff, so writing results into it as they
arrive is what lets a session that has filled its context be cleared
instead of carrying the conversation or re-deriving what was measured. It
asks for the dead ends too, since those are what stop the next session
spending an afternoon somewhere already ruled out.

Adds a "Where things stand" block, because the next agent's first question
is which box is next and the answer was previously spread across the list:
E0, E1, I0a, I0b and I1 done, I2 next with E2 able to run in parallel,
client-core not started, and the two emulator-tools changes made outside
this repo.

Corrects what had gone stale: the next-agent steps still said to start at
E0; the iris section still described a fourteen-gate cosmic-text tree and
called the text stack an open question; and the weight section still spoke
of E1 as something that would happen. It now carries the numbers instead --
43s and 2.1 GB against 1m46s and 1.5 GB for iris, and 181 MB debug against
11 MB release for the Masonry demo. Adds the rule about bounding heavy runs
with a kill timer scoped to the pid, which cost a wrong conclusion here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-04 19:32:41 -04:00
1 parent 1a97d0ef5c
commit 43743ba171
1 file changed
+93 -25
+93 -25
View File
@@ -14,8 +14,50 @@ this clone is where things get tried before anything is committed to
**in-house framework to be built up** for this, with Masonry as the **in-house framework to be built up** for this, with Masonry as the
yardstick it is measured against. yardstick it is measured against.
Decisions get a date and a reason here, the way `PLAN.md` does. Nothing in Decisions get a date and a reason here, the way `PLAN.md` does. A section
this file has been tried yet unless a section says it has. describes something that has been tried only where it says so, with a date.
## 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.
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.
## Where things stand (2026-09-04)
- **Done**: E0 (toolchain), E1 (Masonry on android-view, which found the
keyboard gap), I0a, I0b (iris builds on a pinned nightly and runs), I1
(parley + glyph atlas).
- **Next**: **I2** — iris on android-view. E1 says the first thing to
answer there is why the editor gets keystrokes but no autocorrect or
suggestions, since that is the constraint the framework decision turns
on. **E2** (a transcript in Masonry) can go in parallel in another
session.
- **Not started**: `client-core`, which is item 1 of the recommendation
below and does not depend on the framework choice. Nothing has been
built for it, and it is not one of the numbered boxes — worth picking up
in a session that wants work independent of the emulator.
- **The app itself is untouched.** Everything so far is in `iris/` and in
the rigs; nothing under `app/` or `server/` has changed.
- **Changed outside this repo**, both in `emulator-tools` and both pushed:
`avd_serial` now validates its cache by asking the device its AVD name
rather than by checking the serial is still attached (a recycled port
silently pointed this checkout at another session's emulator), and
`EMU_GPU=software` was added as an opt-in that keeps a run off the host
GPU and gives the guest a software Vulkan device. The default is
unchanged, because `-gpu host` was measured and the Compose benchmarks
depend on it.
## What has to be reproduced ## What has to be reproduced
@@ -317,7 +359,8 @@ Desktop runs on winit. Vello needs a compute-capable GPU or falls back to
[cat16/iris](https://github.com/cat16/iris), read 2026-09-04 from the one [cat16/iris](https://github.com/cat16/iris), read 2026-09-04 from the one
public commit (2026-01-31, "portfolio copy"; ~8,700 lines in `core`, public commit (2026-01-31, "portfolio copy"; ~8,700 lines in `core`,
`macro` and the crate itself). Retained-mode widgets stored outside the `macro` and the crate itself). Retained-mode widgets stored outside the
render tree, `wgpu` 28 directly, `winit` 0.30, `cosmic-text` 0.16, a render tree, `wgpu` 28 directly, `winit` 0.30, `cosmic-text` 0.16 (parley
since I1), a
relative-anchor-plus-offset layout with `rest()` and `rel()` lengths, a relative-anchor-plus-offset layout with `rest()` and `rel()` lengths, a
postfix builder API (`rect(..).radius(30).on(CursorSense::click(), ..) postfix builder API (`rect(..).radius(30).on(CursorSense::click(), ..)
.sized(..).align(..)`), events handled where the widget is declared, and .sized(..).align(..)`), events handled where the widget is declared, and
@@ -325,10 +368,17 @@ a single-threaded context passed explicitly — all of which reads like this
codebase's own rules. There is text editing (`widget/text/edit.rs`), codebase's own rules. There is text editing (`widget/text/edit.rs`),
images, masks, spans and stacks; the TODO names text resizing as images, masks, spans and stacks; the TODO names text resizing as
per-frame slow and scaling as unsolved. It requires **nightly** (fourteen per-frame slow and scaling as unsolved. It requires **nightly** (fourteen
`#![feature]` gates, among them `const_trait_impl`, `unboxed_closures`, `#![feature]` gates as vendored, among them `const_trait_impl`,
`portable_simd`, `associated_type_defaults`). Desktop only; no Android `unboxed_closures`, `portable_simd`, `associated_type_defaults`; eleven
surface, no IME, no accessibility tree, no virtualised list, no rich-text after I0b and I1 — see those steps for the current list). Desktop only; no
selection. Android surface, no IME, no accessibility tree, no virtualised list, no
rich-text selection.
**That list is the work, and some of it is done.** As of 2026-09-04 it
builds on a pinned nightly, runs on this machine's GPU, has parley and a
glyph atlas, and `iris-core` cross-compiles to Android. What it still
lacks from the list above is the Android surface, the IME bridge, the
accessibility tree and the virtualised list — I2, I3 and I4.
**iris is not a candidate to be tested as it stands. It is the in-house **iris is not a candidate to be tested as it stands. It is the in-house
library** (Iris, 2026-09-04): "essentially a good start to a rewrite from library** (Iris, 2026-09-04): "essentially a good start to a rewrite from
@@ -340,16 +390,16 @@ it needs are the same ones Masonry gets from android-view, Parley and
AccessKit, and there is no reason iris cannot sit on those same AccessKit, and there is no reason iris cannot sit on those same
foundations rather than reinvent them — the surface, the keyboard bridge foundations rather than reinvent them — the surface, the keyboard bridge
and the accessibility tree are platform plumbing, not a framework's and the accessibility tree are platform plumbing, not a framework's
identity. Whether the text stack stays cosmic-text or moves to Parley is identity. The text stack was the first real design decision in that work,
the first real design decision in that work (see I1 below). and it is settled: **Parley, with a glyph atlas** (I1, 2026-09-04).
Two things to carry into that work honestly. Nightly is the opposite of Two things to carry into that work honestly. Nightly is the opposite of
"holds up long term": a build that breaks on a toolchain update, on the "holds up long term": a build that breaks on a toolchain update, on the
machine Dev Updater builds on, unattended. Pin a dated nightly in machine Dev Updater builds on, unattended. **Done in I0b**
`rust-toolchain.toml` immediately, and keep a list of which `#![feature]` `iris/rust-toolchain.toml` pins `nightly-2026-09-03` — and the gate list
gates are load-bearing so they can be retired as they stabilise or are lives with I0b and I1, to be retired as they stabilise or are designed
designed around. And a one-person framework carries every gap itself, around; it is down from fourteen to eleven. And a one-person framework
which is what Iris said she is willing to do. carries every gap itself, which is what Iris said she is willing to do.
"From scratch" therefore means iris, not a fourth thing. Masonry stays in "From scratch" therefore means iris, not a fourth thing. Masonry stays in
the plan as the **yardstick and the fallback**: building its demo and its the plan as the **yardstick and the fallback**: building its demo and its
@@ -377,10 +427,18 @@ uses is to optimise dependencies while leaving the app crate at `opt-level
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 2 opt-level = 2
E1 measures this rather than remembering it: cold and incremental build **Measured 2026-09-04, and it is not the widget layer.** iris's own
time, APK size, resident memory at rest and while streaming, and the graph (wgpu + winit + cosmic-text at the time) built cold in **43s** with
frame cost of one 800-event page — for the Masonry demo as shipped, then a 2.1 GB `target/`, and **1m46s** with a 1.5 GB `target/` under the
with the profile above. Vello proper needs compute shaders and carries a profile above — so the knob costs build time and saves disk here, and
plain debug was never the problem. Masonry's graph is the one with Vello,
Parley, Fontique and Skrifa in it, and E1 gives the number that matters
for it: **`libmain.so` is 181 MB in debug and 11 MB in release.** That
size is also a correctness issue rather than only a weight one — a debug
build labels its Vulkan objects, and the emulator's driver segfaults in
`SetDebugUtilsObjectNameEXT` when it does. Runtime cost of the profile
knob is still unmeasured; resident memory and the frame cost of an
800-event page want E2. Vello proper needs compute shaders and carries a
large shader set; `vello_hybrid` is lighter and Masonry can now render large shader set; `vello_hybrid` is lighter and Masonry can now render
through either (or Vello CPU) via its `imaging` abstraction, so "keep it through either (or Vello CPU) via its `imaging` abstraction, so "keep it
light" has a knob inside the same stack. light" has a knob inside the same stack.
@@ -737,18 +795,28 @@ re-derived:
2. Work on the **`rustify`** branch of this clone (`ai-app-2`), not on 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 `main` and not in `ai-app`. Nothing on this branch is production until
Iris says so. Commit and push as you go. Iris says so. Commit and push as you go.
3. Take the next unchecked box above, in order: E0 first, then E1, then 3. Take the next unchecked box above, in order. E1 has proved android-view
the iris track from I0. E-steps and I-steps can proceed in parallel in on this emulator, so the E-steps and the I-steps can now proceed in
separate sessions once E1 has proved android-view on this emulator. parallel in separate sessions; see "Where things stand" at the top for
which is next.
4. Every step ends with its measurement written into this file beside the 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 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". 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` 5. Run the existing rigs rather than inventing new ones: `ui-sandbox.sh`
for a server with fixtures, `transcript-bench.sh` for the scroll for a server with fixtures, `transcript-bench.sh` for the scroll
baseline, `ui-trace` for anything positional, `emu up` for the baseline, `ui-trace` for anything positional, `emu up` for the
emulator. The Vulkan section below says how to get a Vulkan path in the emulator, and `iris/run-headless.sh EXAMPLE --shot PNG` for an iris
emulator when a `wgpu` backend needs one. example on this displayless machine. The Vulkan section below says how
6. Decisions belong here with a date and what was rejected, the way 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. `PLAN.md` does it. Do not put design into commit messages alone.
### Vulkan in the emulator (measured 2026-09-04) ### Vulkan in the emulator (measured 2026-09-04)