Files
ai-app/docs/IRIS_EXTRACTION_HANDOFF.md
T

13 KiB

Iris extraction handoff

Status at 2026-09-13, immediately after pull request #10 was squash-merged. This is an operational handoff requested by the repository owner. Do not treat it as a permanent decisions log; remove it after the extraction is complete.

Stop point

  • Canonical Iris is git@git.arirex.me:iris/iris.git.
  • The agent-owned fork is git@git.arirex.me:iris-ai/iris.git.
  • Canonical main is at 0f6a28b4dd20d1f6bd745271c5f0926028c8fdb6, the squash merge of PR #10.
  • Updated 2026-09-13: two independent PRs are open against canonical main, neither reviewed yet -- #11 (split/11-texture-bindings, the glyph atlas as an array texture and standalone images with their own bind groups) and #12 (split/12-pointer-routing, a scroll falling through a hovered button). Both branch from 0f6a28b and touch disjoint paths, so either can merge first. Their worktrees are /home/bob/repos/iris-pr11 and /home/bob/repos/iris-pr12.
  • /home/bob/repos/ai-app-2 is on rustify at 50d214e before this handoff commit. Its worktree was clean.
  • /home/bob/repos/iris-pr10-text is clean at 983e15c, the merged PR head.

Objective and constraints from the owner

Pull Iris out of ai-app into a useful standalone UI framework even if the Rust application switchover is not accepted. App/session/transcript/server behavior must not leak into Iris; product code belongs in app/, and the dependency is one-way from the app to Iris.

The original extraction PR was too large to review. Rebuild it as small, coherent PRs. Exact commit archaeology is not important, and a slice may be redone instead of mechanically transplanted. It is acceptable for a rewritten slice not to remove every old feature. Independent, non-overlapping PRs may be open concurrently so work can continue while the owner reviews another PR.

The bot cannot push canonical Iris. Work in the iris-ai/iris fork and open pull requests into iris/iris:main. Do not recreate an ai branch in the canonical repository; the fork is the isolation boundary.

Repository topology

ai-app checkout

  • Path: /home/bob/repos/ai-app-2
  • Remote: origin = git@git.arirex.me:iris/ai-app.git
  • Branch: rustify
  • Commit 50d214e replaced the in-tree iris/ directory with a submodule.
  • The submodule is pinned at 32f6ad8c795ea2badf4ade7a51c2c959e8c96921, the complete extracted snapshot.
  • .gitmodules currently points Iris at the bot fork, not canonical Iris: git@git.arirex.me:iris-ai/iris.git, branch main.
  • This URL and the submodule pin will eventually need reconciling with the canonical repository as the split PRs land. Do not change them casually: ai-app currently needs the complete snapshot, while canonical Iris is only partially caught up.

standalone Iris checkout

  • Path: /home/bob/repos/iris
  • origin = git@git.arirex.me:iris-ai/iris.git
  • upstream = git@git.arirex.me:iris/iris.git
  • Local main and origin/main are the complete target snapshot at 32f6ad8. They are deliberately not canonical main.
  • origin/archive/full-extraction also names 32f6ad8 as an explicit safety copy.
  • history/full names the corresponding source-history result at a615bcd.
  • upstream/main was fetched after PR #10 and names 0f6a28b.

Do not reset, overwrite, or force-push the fork's main: it is both the target reference and the commit pinned by ai-app. Start each new PR branch from the current upstream/main in a separate worktree, and consult archive/full-extraction for the intended final state.

Existing worktrees under /home/bob/repos/iris-pr* retain the already merged branches. They are useful references but should not be based on for new work. The old branches remain on the fork; no branch cleanup has been requested.

Gitea access and workflow

  • REST token: /home/bob/.config/gitea/token (never print or commit it).
  • SSH bot key: ~/.ssh/id_ed25519_gitea.
  • The token and account can create/fork repositories and create/comment on PRs.
  • The bot can see inline reviews and ordinary PR comments through the Gitea API.
  • Gitea branch protection can express branch-level restrictions, but the owner chose a fork-and-PR workflow instead.
  • PR #10 was merged using Create squash commit. That put one combined commit on canonical main; the review/fix commits remain on the fork branch.

A safe shape for the next branch is:

cd /home/bob/repos/iris
git fetch upstream
git worktree add -b split/10-short-name /home/bob/repos/iris-pr11 upstream/main

Push the branch to origin, then create the PR from iris-ai:<branch> into iris:main. Never rewrite a pushed branch; follow review with additive commits.

There is no persistent review watcher configured. At the start of a new turn, query open PRs and their reviews/comments before assuming the owner has not responded. The useful endpoints are:

GET /api/v1/repos/iris/iris/pulls?state=open
GET /api/v1/repos/iris/iris/pulls/{number}/reviews
GET /api/v1/repos/iris/iris/issues/{number}/comments

Merged extraction PRs

PR Result on canonical main
#2 Build Iris 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 frames (db9b0f2)
#7 Keep unsafe reference helpers internal (0191f20)
#8 Initialize the window uniform from the surface (6e271e8)
#9 Preserve primitive-count recursion (b90c855)
#10 Move text layout and rendering to Parley (0f6a28b)

3f92894 and bf49525 were the starting points for #11 and #12 respectively. Neither applied cleanly: the base has moved under both, so each was rebuilt rather than transplanted. Of the three commits named under "How to choose the next slices", only 6671194 (widget draw size) is still unclaimed.

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

PR #10 details worth preserving

PR #10 was the first large cohesive slice. Text buffers, shaping, editing, rendered glyphs, and the glyph atlas share types, so splitting it further would have produced artificial broken stages.

The final PR includes:

  • Parley-based text layout and Swash rasterization.
  • Public editing-behavior integration tests in tests/text_edit.rs.
  • Four retained tests: empty-field input placement, selection replacement, UTF-8-aware backspace, and IME preedit replacement.
  • A LayoutKey rather than tuple-shaped cache state.
  • Normal Rust string-range validation for deletion, including panics for invalid ranges instead of silently sorting or clamping them.
  • Glyph rasterization and cache lookup factored into focused helpers.
  • Standard hashing instead of a handwritten FNV implementation.
  • One-pass atlas allocation, checked row copies, Vec2 UVs, clearer binding update naming, and colour state owned by GlyphEntry.
  • Zero-sized and atlas-oversized glyph warnings. Rejected glyphs are negatively cached, so each glyph/font/size/subpixel/variation key warns once rather than every frame. log 0.4.29 was added directly to iris-core; the lockfile was kept minimal rather than accepting unrelated dependency pruning.
  • RenderedText is owned by the cache and borrowed by callers. It does not use Arc: no external shared ownership was needed, and the old design cloned an Arc both on cache hits and insertion merely to return an owned value.

All of these passed:

cargo fmt --all --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked

The final PR branch commits after the main implementation are intentionally small review responses. Do not mistake them for unreviewed extra scope. The most relevant review discussion was summarized in PR comments 85, 90, 92 and 94.

Current Iris TODO retained by the owner

PR #10 deliberately leaves these old questions in the root TODO file:

images
    settings (sampler)
    consider typed TextureHandle<T> variants for distinct texture uses

WidgetRef<W> or smth instead of Id
    enum that's either an Id or an actual concrete instance of W
    painter takes them in instead of (or in addition to) id
    then type wrapper widgets to contain them
    allows for compile time optimization if a widget wrapper's inner is known at compile time
        and the id of inner is not needed anywhere
    maybe introduce InnerWidget trait to allow for editors to expose & modify inner type
    maybe could also store a parent widget and keep using InnerWidget trait? unsure if possible

vecs for each widget type?

POTENTIAL BUG: closures that store IDs will not decrement the id!!! need to not increment id if moved into closure somehow??? wait no, need to decrement ID every time an event fn is added...... only if the id is used in it..??

Do not prune these merely because newer architecture looks adjacent to them. The owner explicitly restored them after an over-aggressive TODO cleanup.

Review and code-quality expectations

Every slice should get a sanity review, not merely a successful transplant. Fix obvious smells, poor naming, needless allocation, awkward state shapes, and accidental complexity while keeping the slice focused. It is not necessary to fully understand or redesign every complicated subsystem.

Comments require special scrutiny. Before submitting or updating a PR:

  1. Enumerate every source comment added by the branch.
  2. Remove comments that paraphrase code, narrate history, explain obvious control flow, or exist only because an agent habitually writes comments.
  3. Keep only constraints, non-obvious reasons, invariants, and genuinely useful API documentation.
  4. Re-check comments after responding to review; PR #9 needed a follow-up solely to remove a trivial comment.

Tests follow the same restraint:

  • Remove redundant tests that restate implementation details.
  • Put valuable public behavior tests in Rust-standard top-level tests/.
  • Keep inline unit tests only when private internals genuinely require them.
  • A small change does not automatically need a new harness.

Respond to review findings with either a code fix or a concise explanation. Do not leave unexplained resolutions, and do not add a ceremonial PR comment when the changed code already answers the review.

How to choose the next slices

Compare canonical main with the archived target:

cd /home/bob/repos/iris
git fetch upstream
git diff --stat upstream/main..archive/full-extraction
git diff upstream/main..archive/full-extraction -- <focused paths>

The archived snapshot is a reference, not a patch to apply blindly. Recreate a change on top of canonical main, exclude app-specific behavior, run the code quality pass, and verify it independently.

Broad remaining areas in the target include:

  • Texture binding/atlas resource organization.
  • Widget draw-size and measurement cleanup.
  • Pointer routing and shared input behavior.
  • Frame diagnostics, staging allocation, and arena delta uploads.
  • 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.
  • Animation timing, dirty-widget processing, and retained-frame allocation cleanup.

Useful source-history commits immediately after the original Parley work include 3f92894 (texture bindings), 6671194 (widget draw size), and bf49525 (pointer routing). They are possible starting points, not guaranteed PR boundaries. Later clean target commits can be listed with:

git log --reverse --oneline --first-parent 05c0ac8..archive/full-extraction

Choose independent path sets when opening concurrent PRs. Avoid stacking PRs unless a real dependency forces it; otherwise each should branch from the latest canonical upstream/main and be reviewable alone.

Project boundaries and verification

Read /home/bob/repos/ai-app-2/AGENTS.md and the machine-wide rules before working. In particular, read the installed Android and graphics machine skills before Gradle, adb, emulator, screenshot, UI-trace, or renderer work.

For a normal Iris framework slice:

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

Use --workspace when the slice touches workspace crates, as PR #10 did. Any rendering claim needs the appropriate real run or focused rig in addition to compilation. Never infer phone frame performance from the debug emulator.

Keep Iris generic. Session drivers, transcript presentation, setup/server concepts, app icons, and product-owned font assets stay in ai-app. Android and desktop code is valid Iris work only when it is a generic framework host or framework-level platform integration.

Final cautions

  • Preserve the dirty-worktree rule even though the recorded worktrees were clean: changes found later may belong to the owner or another agent.
  • Do not force-push or rewrite any published branch.
  • Do not delete the archived full snapshot or the fork main that ai-app pins.
  • Do not change the submodule remote to canonical until canonical contains the commit ai-app needs, or until the owner explicitly accepts a temporary non-building pin.
  • Continue using small PRs; the existence of a complete target branch is not permission to recreate the original giant PR.