Files
ai-app/docs/IRIS_EXTRACTION_HANDOFF.md
T
irisandClaude Opus 5 912602d52b Order the remaining Iris slices by how fundamental they are
The owner asked for library updates and core framework changes first, so code
is only written once, and for tests to arrive with a slice rather than as
scripts outside the repository. So wgpu 28 -> 30 and the headless rig and
harness come before any of the feature slices.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 17:45:34 -04:00

12 KiB

Iris extraction handoff

Operational handoff for pulling Iris out of ai-app into a standalone framework. Not a decisions log; delete it when the extraction is done.

Read this first

Pull request #11 has been reviewed seven times; the seventh is answered in 79dcc15 and an eighth has not arrived. Check for one before doing anything else:

TOKEN=$(cat ~/.config/gitea/token)
curl -s -H "Authorization: token $TOKEN" \
  https://git.arirex.me/api/v1/repos/iris/iris/pulls/11/reviews
curl -s -H "Authorization: token $TOKEN" \
  https://git.arirex.me/api/v1/repos/iris/iris/pulls/11/reviews/<id>/comments
curl -s -H "Authorization: token $TOKEN" \
  https://git.arirex.me/api/v1/repos/iris/iris/issues/11/comments

Check the newest submitted_at rather than assuming the first result is current. My replies are ordinary issue comments on the same PR and say what each change was for.

Agree a design before sending another variation of it. The owner stopped the fourth round with "we should probably agree on the design here rather than you keep submitting variations that I review". When a review comes back about the shape of something rather than a defect in it, put the options and a recommendation in front of her and implement what she picks. She chose the per-image bind group for textures on 2026-09-13; the alternatives were packing images into arrays like atlas pages, and a bindless binding_array (ruled out by Android support).

Stop point

  • Canonical Iris: git@git.arirex.me:iris/iris.git, main at 0f6a28b (the squash merge of #10).
  • Agent fork: git@git.arirex.me:iris-ai/iris.git. The bot cannot push canonical; work in the fork and open PRs into iris/iris:main.
  • #11 split/11-texture-bindings, worktree /home/bob/repos/iris-pr11, head 79dcc15, fourteen commits on top of 0f6a28b. Reviewed seven times, all answered.
  • #12 split/12-pointer-routing, worktree /home/bob/repos/iris-pr12, head 028521b. A scroll falling through a hovered button. Not reviewed as of the last check. Independent of #11 and can merge in either order.
  • /home/bob/repos/ai-app-2 is on rustify, worktree clean.

What #11 became

It started as "draw the glyph atlas as an array texture" and four rounds of review turned it into a rework of how primitives are drawn. Worth knowing before reading a new review, because the earlier rounds are what produced this shape:

  • Atlas pages are their own thing. GlyphAtlas owns its page images and emits dirty rectangles; GpuPages owns the array texture they upload to. Textures holds standalone images only.
  • A primitive registers itself by being drawn. The type carries its own WGSL and PrimitiveRegistry keys ids by TypeId, so the kind comes from the type and there are no RECT/GLYPH/TEXTURE constants. Nothing is seeded, so an id depends on what a ui drew first and a ui pays only for what it uses.
  • Nothing dispatches dynamically. Push, free, renumber, upload and draw are identical for every primitive; only the entry size and the pipeline differ, and those are data. InstanceList carries a runtime stride and its data as bytes. Measured: 0.2ns per write over a statically typed list, against 1.6ns for a trait object.
  • The shared group is what every pipeline gets: the window and the masks. A mask texture would go here too.
  • Primitive::SAMPLES says what a primitive samples and how often it binds -- Atlas once for a list, Image(fn) for one instance alone, defaulted to nothing. That is group 2, so a rect's pipeline has no texture or sampler in its layout. Both cases share one layout builder and one group builder.
  • shader.wgsl became shader/prelude.wgsl plus one file per primitive, because one module cannot declare two types at the same binding. The prelude carries only what every primitive uses -- window, masks, vertex shader, masked() -- and its header is where binding numbers are written down; what a shader samples is declared by that shader.
  • A texture handle is drawn like anything else. Painter::primitive takes impl PrimitiveLike: a primitive, or something that yields one and does what else drawing it needs -- a &TextureHandle retains its share on the way through. The blanket impl and the one for &TextureHandle do not collide, because Primitive is ours and no crate can add the overlapping impl.
  • Order within a layer means nothing, and the widgets do not rely on it: Stack gives each child its own layer and TextEdit draws its view in a child layer above the selection rectangles. Verified with a ui that registers glyph before rect.

Standing instructions from the owner

  • Pull Iris out even if the Rust application switchover is not accepted. No app, session, transcript, setup or server concepts in Iris; the dependency runs one way from app/ to Iris.
  • Small, coherent PRs. The original extraction PR was too large to review. A slice may be redone rather than transplanted, and need not remove every old feature. Independent PRs may be open at once so work continues during review.
  • Do not recreate an ai branch in canonical Iris; the fork is the boundary.
  • Never rewrite a pushed branch. Follow review with additive commits.
  • Respond to each review finding with a fix or a concise explanation. Do not add a ceremonial comment when the changed code already answers it.

The review gate

~/.claude/MACHINE.md now carries a machine-wide rule, and the procedure is the installed pre-submit-review skill. It is not optional and not project-specific: build clean, review the code, review the comments separately once the code has settled, then verify the claim by running it. The fixes a review produces are themselves unreviewed code, so the passes repeat until a round finds nothing.

Running it on #11 found four defects that format, clippy, tests and five headless renders had all passed — including one hidden by the tests passing, where an image alone in a layer failed GPU validation because every other case happened to have a rectangle in the same layer leaving a bind group set. That is the shape to hunt for.

audit.sh in the skill directory prints every comment line a branch adds against a base ref, for an explicit verdict each. The owner's standing complaint is verbose agent comments; the default verdict is delete.

Repository topology

ai-app checkout

  • /home/bob/repos/ai-app-2, origin = git@git.arirex.me:iris/ai-app.git, branch rustify.
  • iris/ is a submodule pinned at 32f6ad8, the complete extracted snapshot, and .gitmodules points at the bot fork, not canonical Iris.
  • Do not change either casually: ai-app needs the complete snapshot while canonical Iris is only partly caught up. Reconcile when canonical contains what ai-app needs, or when the owner accepts a temporarily non-building pin.

standalone Iris checkout

  • /home/bob/repos/iris, origin = fork, upstream = canonical.
  • Fork main and origin/archive/full-extraction both name 32f6ad8, the target snapshot. history/full names the source-history result a615bcd.
  • Do not reset, overwrite or force-push fork main: it is both the target reference and the commit ai-app pins.
  • Start each new branch from current upstream/main in its own worktree:
cd /home/bob/repos/iris && git fetch upstream
git worktree add -b split/13-name /home/bob/repos/iris-pr13 upstream/main

The other /home/bob/repos/iris-pr* worktrees hold merged branches. They are readable references; do not build new work on them.

Merged so far

PR On canonical main
#2 Build on the current nightly (4275314)
#3 Request a frame after resize (936fbdd)
#4 Decouple iris-core from winit (465e430)
#5 Use vsync by default (ec2b5d4)
#6 Notify winit before presenting (db9b0f2)
#7 Keep unsafe reference helpers internal (0191f20)
#8 Initialize the window uniform from the surface (6e271e8)
#9 Preserve primitive-count recursion (b90c855)
#10 Text layout and rendering on Parley (0f6a28b)

URLs are https://git.arirex.me/iris/iris/pulls/{number}.

Choosing the next slice

Most fundamental first, from the owner on 2026-09-13: "please do more fundamental changes first, such as library updates and core framework changes, so that code only has to be written once", and "should probably start adding tests early on rather than later, so you don't have to make separate test scripts and stuff. This might involve making the harness eventually, depends on what needs tested."

So the order is by how much else depends on a thing, not by what is nearest ready, and a slice lands with tests rather than with a script in /tmp.

  1. wgpu 28.0.0 -> 30.0.1. Two majors behind, and it is under everything the renderer does; any renderer slice written first is written twice. winit is at 0.30.12 with only a 0.31 prerelease available, so it stays until that is stable; image 0.25.9 -> 0.25.10 is a patch to take at the same time. parley 0.11.1 is current.
  2. The headless rig and the test harness -- scripts/run-headless.sh, headless.conf, rig-input and iris::harness live in ai-app's iris submodule, not in canonical Iris. Until they are extracted, canonical Iris can only be tested by the four tests/text_edit.rs tests, and anything about rendering is verified by hand from another checkout. Extracting them is what lets every later slice arrive with tests.
  3. Everything below, once those two are in.

Still in the target and not yet claimed:

  • Widget draw size and measurement cleanup (source commit 6671194).
  • Retained span, scrolling and layout placement.
  • Retained paints, selection, overlays and shared UI runtime state.
  • Generic desktop/Android framework hosts and reusable example/APK tooling.
  • Application-owned fonts and application-named font families.
  • Shared resource-handle bookkeeping and replaceable glyph-atlas buckets.
  • Positioned text overflow and cluster-safe ellipsis.
cd /home/bob/repos/iris && git fetch upstream
git diff --stat upstream/main..archive/full-extraction

The archive is a reference, not a patch to apply. Recreate a change on top of canonical main, leave app-specific behaviour out, and verify it independently. Pick disjoint path sets when two PRs are open, and branch each from the latest upstream/main rather than stacking.

Verifying a framework slice

cd <iris-worktree>
cargo fmt --all --check
cargo clippy --all-targets -- -D warnings
cargo test

--workspace when the slice crosses workspace crates. A rendering claim needs a real run, which on this machine means the headless rig in ai-app's submodule:

cd /home/bob/repos/ai-app-2/iris
./scripts/run-headless.sh tabs --dir /home/bob/repos/iris-prNN \
    --shot /tmp/out.png --seconds 3
./scripts/run-headless.sh tabs --dir ... --replay /tmp/taps.touch --shot ...

A .touch file is <ms> down|move|up <x> <y> in the output's own pixels; the script replays it through a virtual pointer. The tabs example's five tabs sit at x = 192, 576, 960, 1344 and 1728 on a 1920x1200 output. Read the this-machine-graphics skill before trusting any of it — the Vulkan device here comes and goes, and everything silently falls back to software when it does.

Cautions

  • Read /home/bob/repos/ai-app-2/AGENTS.md and the machine-wide rules first.
  • Keep Iris generic: session drivers, transcripts, setup and server concepts, app icons and product fonts stay in ai-app. Android and desktop code is Iris work only when it is a generic host or platform integration.
  • Preserve the dirty-worktree rule. All worktrees were clean at handoff; anything found later may be the owner's or another agent's.
  • Do not delete the archived snapshot or the fork main ai-app pins.
  • A complete target branch is not permission to recreate the giant PR.
  • Another agent was freeing disk on this VM and removed target/ from the iris-pr* worktrees once. Sources and git state were untouched. Tell peers before changing shared machine tooling, and expect a cold rebuild sometimes.