Files
ai-app/docs/IRIS_EXTRACTION_HANDOFF.md
T
2026-09-13 16:53:00 -04:00

10 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 five times; the fifth is answered in 01a9b86 and a sixth 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 01a9b86, ten commits on top of 0f6a28b. Reviewed five 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: window, masks, the glyph atlas array, and the one sampler. A mask texture would go here too. A primitive that samples an image of its own declares Primitive::TEXTURE, which gives it an image bind group, a draw call per instance, and the slot recorded at write time; no other pipeline mentions textures at all.
  • 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 records the group 0 binding numbers; the atlas and sampler are declared by the shaders that read them.
  • 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

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.

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.
  • The headless rigscripts/run-headless.sh, headless.conf and rig-input live in ai-app's iris submodule and not in canonical Iris. Everything below depends on running them from there; extracting them is a slice of its own.

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.