00063a7f7c
HEAL painted a grey blob over the photo it was meant to repair, and the cause was one draw Skia gets wrong on this build: a snapshot used as a SCALED source comes back with the alpha multiplied into its rgb a second time, so a partly transparent rim — which is what the mask's feather is — was read as a dark wash. A pyramid of halvings is built of exactly those scaled draws, which is why the repair's own rim came out grey; the same source read at 1:1 is exact (measured, tmp-src-probe). So the fill is now a cascade of blurs, all of them 1:1: the cut frame is blurred at a few widths, widest first, each finer one drawn over it, the untouched cut on top. The finest is one brush wide, each next is twice the last, and the count follows the depth of the hole (the stroke's own diagonal against the widest brush), which is what keeps the middle of a long hole filled rather than bare. Nothing is copied from anywhere and the rim arrives with no seam, which is what the passes were always for. And the fill is built on the brush's own box rather than the frame, padded by three sigma of the widest level. The composite reads it only where the mask is, so this changes no pixel of the result (heal-check measures the same masks either way) and it is what makes the repair affordable at the zoom a repair is aimed at: at a 3200px preview a single spot went from 1.7s of frozen main thread to 1.2s, and the heal's own share of that from 0.8s to 0.3s. What is left is the pipeline itself, which is a separate matter. The FIX brush outlived its tab. It takes the pointer on the photo, and leaving TOOLS never put it down — so its layer stayed on the stage over a photo nobody was editing, and its ring came back under the pointer every time the hand crossed a repair, which reads as the tool re-arming itself. It now goes down with the tab, with another chip or another strip, and on a press outside its own two chips, the same way the mask tool does. And the Immich button was missing for a visitor who had just connected a server: the studio and the library are two screens of one page, both mounted, so coming back is a route change and not a mount — the studio's own effect, which reads the server list once a session, never ran again. It is read again on a popstate that lands on /app. Checks: node scripts/heal-check.mjs (the cascade and the box, to the letter), npx tsc --noEmit, and the studio driven in a browser: the button appears on the way back from the library, the brush goes down with TOOLS and re-arms with HEAL, and a stroke over a photo lays one band with no grey in it.