Files
ai-app/docs/HANDOFF.md
T
2026-09-20 02:10:22 -04:00

8.1 KiB

Handoff

Where the work in flight stands. The settled layout design, the vocabulary and the measurement method are in docs/LAYOUT.md; what the review of #19 found is in docs/LAYOUT_LOG.md.

The Iris layout repair is submitted

Iris PR #19 (layout/one-ask) replaces closed #18. The tip is b7b8d09, and past the reviewed cadfba0 it is eight rounds, each described in docs/LAYOUT_LOG.md:

  • The repair, add6774 and 84dad21 -- collapsed-share placement, retained mask ownership, a redraw-on-reparent defect, and repeated work in the test harness.
  • The vocabulary and the container API, 5642f20 through 58ce74d.
  • Naming, 55df32a through 40b89c1.
  • A sweep over the logic those names exposed, 8d2b7a5 and 6c84b6f.
  • A clarity sweep, 3da1c71 through 1ebd4d3 -- naming the pairs layout returns, Span::slot, a diagnostic that printed the rel base while calling it the box, and in_parent matching a place's own cases.
  • A quality sweep, aea0387 through 69ba915 -- a kept contract judged against the placed box rather than the box asked about, two things nothing read, three reuse rejections the diagnostics could not see, and a fuzz case that ran only in the long scan.
  • A sweep over the renderer, the text store and the retained path, d8d5122 through 1096c31 -- a contract kept where the new window left it out, a surface configured under its own texture, a counter naming the wrong contract, things nothing reads, and a question asked through a value rather than a reference.
  • A sweep over the shader boundary and the position widgets, b7b8d09 -- two constants the shader and the CPU both count in written twice, and a Scroll positioning content the framework positions, which cost a redraw at the default alignment.

The settled design of the last three is in docs/LAYOUT.md under "Three names, and the one argument that says them". Bryan settled the API over 2026-09-17 to 19; it is current, not frozen.

The core design remains sound. Round-to-nearest is still unchanged.

Two checkouts share one Git storage: /home/bob/repos/iris is the active layout/one-ask worktree, and ai-app-2/iris stays on main at the app's 32f6ad8 pin until the integration below is ready.

The base is upstream/main, not main

Diff this branch against upstream/main. The local main is the wrong base and gives a plausible-looking wrong answer. PR #19 is iris-ai/iris:layout/one-ask into iris/iris:main, which is the upstream remote, at ca2b4b2. Local main tracks origin/main -- the fork's line, which carries the app's own commits, is not an ancestor of upstream's main, and sits four merged pull requests behind it (#10 parley text, #12 pointer routing, #16 draw/size merge, #17 headless rig).

git diff main...layout/one-ask             # WRONG -- base 7b54aaf
git diff upstream/main...layout/one-ask    # right -- base ca2b4b2

git iris-base and git iris-diff are configured in this checkout and use the right one. The authority is gitea, when it matters:

curl -s -H "Authorization: token $(cat ~/.config/gitea/token)" \
  https://git.arirex.me/api/v1/repos/iris/iris/pulls/19 \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["base"]["sha"])'

Nothing here can be fixed by renaming: main is the app submodule worktree's checked-out branch at its 32f6ad8 pin, and the two worktrees share one ref store. Git has no way to record a pull request's base, so the note is the mechanism.

This has already cost real work. The sixth sweep reviewed against local main for half a session (Bryan caught it, 2026-09-20), and reported the parley migration's undo path as this branch's. The fourth and fifth sweeps deleted Painter::text_data, ActiveData::size_deps and SizeRule::apply partly on the same false reading; the deletions stand on their own merits -- text_data had no caller at ca2b4b2 either, and size_deps was read there and orphaned by this branch's rewrite -- but "arrived on this branch" was not the reason for all of them.

How to check a round

Always, because they cost nothing: format, workspace clippy under -D warnings with and without layout-diagnostics, the workspace tests, and the cold dump. layout_dump over 400 depth-5 trees is 34,492 boxes, and it is the only thing that catches two same-typed values being swapped, which is the failure mode of a rename or a move. The repair moved 650 of those boxes, all from the collapsed-share correction; every commit since has been byte-identical to 84dad21.

Only when the change can alter what layout computes: the three seed scans -- 400 at depth 5, 1,000 at depth 6, 2,000 at depth 4. They cost about a quarter of an hour and they exist to find logic that is wrong on some tree shape, so a rename has nothing for them to find (Bryan, 2026-09-19). Never start one and then edit the tree: cargo rebuilds mid-flight and exits 1 from a compile error, which reads exactly like a fuzzer failure.

What is next, in order

  1. Bryan's review of #19. Fixes are themselves unreviewed code: repeat pre-submit-review over each round's changes, and apply the gate above to whatever each one touched. The ordinary oracle does not replace absolute geometry and retained-primitive expectations.
  2. A review of everything written before the review gate existed. pre-submit-review and the rule that nothing is submitted unreviewed arrived on 2026-09-13, well after the Rust port and most of Iris were written, so all of that code went in unreviewed and none of the five sweeps above covered more than the layout branch. It wants a pass of its own (Bryan, 2026-09-20). The surface-texture defect in 02048ea is the argument: nothing about that arm was hard, and it was written wrong anyway, which is what a first reader catches and a later sweep of some other subject does not.
  3. Integrate the app's Iris capabilities before changing its pin. 32f6ad8 has 45 commits not reachable from the review branch; shared UI ownership, richer masks, Android support, and app-side performance work must survive the integration. What has to survive is those capabilities, not the calls the app makes today: the app is to be largely rewritten against the new API rather than ported call by call, so nothing in Iris is kept alive for the app's sake (Bryan, 2026-09-20).
  4. Round-to-nearest, CPU and shader together as one verified change. Bryan approved it on 2026-09-17 and neither half has landed; the derivation, the form to use and what to re-check are in docs/LAYOUT.md under "Rendering the grid (pending)".

Wanted but not started, recorded in iris/TODO: transforms on a move entry, so a whole subtree scales or rotates with one buffer write and no redraw. Compose-style stretch at the end of a scroll area is the use that prompted it. A move entry only translates today, and composing through one scales the rel part while px passes through untouched, so fixed-size content and glyphs do not follow a shortened entry.

Smaller layout items, none urgent

  • Nested spans pass leftover weight up, so three leftover children in one inner span beside one in another get three quarters to one quarter. No other layout system does that; confirm it is wanted.
  • A span can overflow itself without bound, so boxes of negative length reach children and nothing states what a widget may assume about one.
  • Fixed::div by zero answers MIN/MAX while ratio answers ZERO; both are caller bugs under debug_assert, but the fallbacks differ.
  • docs/LAYOUT.md §4, §5 and the density section name Painter::place, Painter::region(), SetSize, desired_width, apply_rest, Len::dp, Aligned and MaxSize, none of which exist. Do not restore OnResize::Translate or OrthoSize.
  • LazySpan, then SizeRule::{Min, Max, Clamp}. A cap may not contain leftover; whether Max narrows the child's drawing box is a product decision.
  • Scroll taking a direction rather than one axis.

Other product work is in docs/PLAN.md and the focused documents it links. Do not mix it into the Iris layout branch.