The owner (2026-11-03, "choose based on performance"): the parts by part ON by default (desktop Wikipedia 11.4
against 10.8 fps by finger, the long page 19.4 against 19.2; JIHAD_GPU_PARTS_PER=0 or <state>/no-parts-per turns
it off), the express lane's second frame on the first in flight OFF (11.4 against 11.4, 18.1 against 19.4;
JIHAD_GPU_EXPRESS_SECOND_PARTS=1 turns it on), and item 4 next over the damaged-rows read. The daemon rebuilt
and pushed to the three variants (ba9bd159), default-check x3 passes; the desktop daemon tests pass.
PICKUP carries item 4's starting design: the tiles' CPU pixels reachable on the compositor side, the band
condition "tiled painted or colour layers only" rather than "scroll only", and the scale at Wikipedia's fit
zoom (0.69 with the tiles at resolution 1) as the owner's decision before the build.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The owner's "cheaper part pictures" (2026-11-03). Engine 0073: each rect of a frame's parts list carries a
signature of its own (its outermost fixed or sticky layer's subtree, every subtree layer's last paint as the
stamp of one up-only counter, the part's scale, quarter-pixel phase, place inside its rect and size);
compose_post3/start3 (overview_ok 4) take the signatures the daemon holds and the pass reads only the rects
not in the list; the poll's 54 ints carry each rect's signature and the rects read. The daemon keeps eight
part pictures by signature and, with JIHAD_GPU_PARTS_PER=1 (or <state>/parts-per), sends the list; without
it E2's rule. The express lane's second frame on the first in flight is back when both are without their
parts (JIHAD_GPU_EXPRESS_SECOND_PARTS=1 or <state>/express-second-parts): its dress puts back what lay
beneath the first's parts and beneath the first's base's parts from the slot records (serials, the rows
read, a slot's previous record kept aside), never on a first that is itself two deep. JIHAD_GPU_VERIFY=1
now verifies the express lane's frames (the reference composed before the job; verifyCompare shared with the
main lane) and JIHAD_GPU_TEST_DRESS_HOLD=<pre|mid>:<ms> holds a first frame's dress so the second's copy
catches it. Both switches off by default: the defaults are the owner's call.
By finger: desktop Wikipedia 10.8 -> 11.4 fps flowing (its contents panel, the part that changes, is 310 of
the 340 KB: the lever is bounded by the changing part's size), the long page 19.2 / 19.4 / 18.1. Verify after
the pan mark: 0 wrong rows in every run on both pages, 142 two-deep frames among them, 55-80 of those with
the first's dress held (docs/rounds/t505-2026-11-03/notes.txt).
The inspector's review (Codex out of credits): REVISE, seven findings, all fixed (impl-review-findings-t505-
cpuparts.md, review 3). The verify then exposed a latent main-lane fault from review 2's F4 (2026-11-02): a
second job whose in-flight base the parts rule refused built on the newest finished frame but told the engine
it was the previous composite, so the frame was recorded 316 rows off and the next own-slot frames copied
wrong rows; fixed (srcInFlight in gpuJobStart; 0 wrong in three runs after).
Also: build/desktop/build-goanna.sh takes a jobs argument like the ARM script (the host short of memory);
tools/device/t502/README.md documents the switches; the kit's R4 criterion, the build site's T-505 row and
PICKUP's top section carry the state; the three variants run this head (default-check x3 passes).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The top section for the update: the state (main 62af6e82, the fork's two branches to push), what
update-engine.sh did, what the desktop tests, the device checks and the certificate checks showed,
the device's packages, the open work in the owner's order, and the session's traps. The handoff
prompt for a fresh agent (.cavekit/handoff-2026-11-03) and push.sh, which pushes both engine branches
before main and reads all three back.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The top section for 2026-11-03: the state (main c29f396c, engine 1f54df3762, the push owed), the wrong
verify frame's cause and fix, the inspector's review, the device on engine 0073 in all three variants
(default-check passes), the traps learned, and the open work in order. RB_20261027 (12 commits): the
certificate commits are already on the device through the roots of 2026-10-25, the memory-safety
fixes are not; the owner's word of 2026-11-02: the update now, patch 0060 dropped, special attention
to the certificate/HTTPS changes (the engine-release kit's R1 gains that criterion; T-551 READY).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The owner stopped the session ("stop here and prepare the directory / cavekit 3.1 files for a
fresh agent"). PICKUP's top section: T-505's state (the parts on the CPU, exact with engine 0072),
the open items in order (the Codex review of 81f56bef/446b22790a the stop cancelled, the long
page's one wrong verify frame, cheaper part pictures, the CPU-composed bands, then the four kits of
Tier 91), the unpushed commits (GitHub main d9816d2f, the fork 078a0942), and the device: all three
variants on this head's engine and daemon, default-check passing in each. The review findings'
status filled in; tools/host/flow-fps.py (the rounds' fps-while-frames-flow counter) kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtfxSpM1w2uiVKeLKoV3g3
The owner's sequence: a Codex adversarial review (context/impl/impl-review-findings-t505-codex.md), then
"prove the cause first": JIHAD_GPU_NO_PUBLISH=1 (a diagnostic: frames made and read, never handed to the
card) shows the same daemon work making desktop Wikipedia 32 fps and the long page 53 while LunaSysMgr draws
nothing, 8.6 / 13.6 while the card moves: the card's redraw on the shared GPU and memory makes every daemon
stage ~3x slower. Then the card's paint measured (the adapter's [paint-stat] lines under its paint log): a
~10 ms software blit (Piranha's PSoftContext2D) of the whole view ~20 times a second, mostly repaints; a
PGSurface kept per frame changed nothing (not kept). The frame is now wrapped without alpha (every path
writes alpha 255): the blit 9 -> 5 ms, LunaSysMgr's card instance -15 % CPU while panning, the picture the
same; <state>/alphawrap reverts. The pan rate is unchanged; the owner's plan is the CPU/GPU split next.
tools/host/express-jobs.py also reads scripted pans.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtfxSpM1w2uiVKeLKoV3g3
The owner's bounded overview. Right after a zoom in that raises the resolution, the express thread posts, all
at once and while the layer tree still holds the tiles at a resolution of 1, two or three windows (a frame's
rows each at half the zoom: the kept frame's scroll and a frame below and above, the scrolled content alone,
read with their alpha so rows no tile covered are left out) and the page's fixed and stuck parts alone at the
kept frame's scroll (with their alpha). At the zoom back, when the kept frame is off the card's view and the
picture covers it, the frame is the picture at twice its size with the parts drawn over it where they sit at
the new position. Desktop Wikipedia by finger: 40-56 ms where the paint's frame came at 294-614 ms; a pan past
the picture still waits. An inspector review's six findings fixed (the readback forced the alpha, the parts
carried the old background, the sticky probe, the scratch's lifetime and size, past-the-last-window placement,
one-to-one matching). Cost: the zoom in's sharp frame 682-779 ms against 519-545.
T-505 measured read only (the owner's "measure first"): tools/device/t502/finger-pan.sh and
tools/host/express-jobs.py; desktop Wikipedia 9.5 fps and the long page 16.6 under a finger, bound by the
readback path (docs/rounds/t505-2026-10-26/notes.txt).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtfxSpM1w2uiVKeLKoV3g3
The 480 px spinning cube, the sensors running: 25.3-28.0 fps on the card (was 16.6-22.0), 360 px 28.9-29.9,
240 px 29.4-30.0 (the page's own ~30); verify with a ticker beside the canvas and the cube stopped 0 rows wrong.
- The frame trailer's dirty words (render/browserserver/Src/JihadFrameWindow.h): before each msgPainted the
daemon writes the rect that differs from the frame published before it and both frames' numbers — the
union of every composite's change since, at that frame's geometry; a forgotten job, popups, a software
frame or an earlier-posted job not yet polled make it whole (BrowserPageGoanna pubDirtyAdd/pubDirtyTake,
publishWindow; JIHAD_GPU_NO_DIRTY=1 off). The adapter, showing that base frame at the same geometry, zoom
and scroll, invalidates only the rect (BrowserAdapter::msgPainted, BrowserOffscreen::dirtyRect). The YAP
messages and the header are unchanged; docs/IPC-CONTRACT.md now describes the trailer.
- Engine patch 0063 (bda31412): a change too tall for the 256-row band surface takes a 512-row band surface
(JIHAD_GPU_BAND_TALL) instead of the frame-sized target; frames are packed for the band surface first, so
desktop Wikipedia's pan frames stay where they were (its pan unchanged). Pins moved; 63 patches reproduce.
- The PDK adapter had not built since T-540: YapProxy.h's member initializer (m_sendFailed) is C++11 and the
PDK's gcc 4.3.3 refuses it; initialized in the constructor. The YAP contract check passes.
- The pan's 56 ms+ gaps traced: tools/host/gap-trace.py, and with JIHAD_GPU_TIMELINE=1 the express lane notes
why it made no frame and every trailer is logged. Desktop Wikipedia's 10 s pan: 24 gaps, all the compositor
job's (a whole read after a tree change, the copy from the base, a job behind the page's tile uploads).
- default-check.sh reads across the daemon log's 2 MB rotation; gpu-spike-collect.sh removes the frame dumps.
- Kit R8's 24 fps criterion ticked, R3's gap criterion annotated; T-511 DONE; notes, README knobs, ENGINE-SOURCE
0063, PICKUP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured with the emulated finger on desktop Wikipedia, the sensors running: the first frame painted at a new
zoom's resolution 593 ms after a double tap in (was 2212), 669 after a double tap out (1890), 513 after a pinch
in (1660), 551 after a pinch out (746); the first frame at the new zoom stays 58-105 ms.
- The critical displayport (GoannaRenderPage::SetGpuDpCritical, BrowserPageGoanna::gpuCriticalEnd): a zoom that
changes the resolution paints what the card shows first, no margins; the margins once a frame of that paint
is out (2 s at most). JIHAD_GPU_NO_CRITICAL=1, JIHAD_GPU_CRIT_MARGIN=<CSS px>.
- The express lane publishes a main-lane frame composed while the main thread is away in the engine (40 ms,
JIHAD_GPU_EXPRESS_TAKEOVER_MS): it polls the job, publishes its slot and takes it off the main lane's list
(a double tap's repaint sat composed 845 ms behind a 1.1 s pump of the page's script). A main-lane post
kicks the express thread; it looks every 3 ms while a main-lane frame is in flight.
- The zoom hold ends when the express frame is posted, not published; a zoom out past half the painted
resolution makes no frame from the old tiles (471 ms of minified draws for a mostly blank frame).
- Engine patch 0062 (63c20e39): PaintFrame's DecideScrollableLayer replaced the root's displayport base with
the composition size at the origin on every paint, so 0059's base (what the card shows, at the shift, times
the resolution) never reached a paint; the GPU path now keeps it. Pins moved (62 patches reproduce the pin).
- JIHAD_GPU_ZOOM_DUMP_SEQ=1; tools/device/t502/finger-zoom.sh (+ finger-zoom-device.sh); zoom-seq.sh ROUND_DIR.
- Pans unchanged or better (long page 29.5-35.4 fps, desktop Wikipedia 22.3-28.5), verify 0 structurally
wrong; ENGINE-SOURCE rows for 0059-0062; docs/rounds/t527-2026-10-25/notes.txt; kit R6 annotated (still open:
the double tap out's first frame within 250 ms); PICKUP's delta.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The emulated finger (tools/device/finger: tap, dtap, hold, drag, drag2, flick, pinch, sleep, raw) drove the
scrolling and gesture items of docs/FINGER-TESTS.md in Enyo, Mochi and Mojo; every result is on its item and
the data in docs/rounds/finger-2026-10-24/zoom-grey.txt.
Found and fixed (the owner's "areas turn grey" of 2026-10-18):
- A double tap on desktop Wikipedia fitted a 104 px infobox cell (6.2x) and the engine's default maximum-scale
of 10 passed the adapter's cap: the smart zoom's block is at least a third of the card wide, and a viewport tag
without maximum-scale reports 4.0 (GoannaRenderPage).
- At rest the zoomed frame lost its right third from a zoom of 2.0 and everything from 2.98: once the pres shell
has a resolution, Gecko takes the root's displayport base in layout device px and divides base + margins by the
resolution, and nsGfxScrollFrame divided the root's display-list clip too; the scroll port handed over in CSS px
shrank to 479 px at 2.33, short of the card's view 531 px over (the engine has no sideways range on a page laid
out at the card's width). jihad_layout_set_displayport2 takes the card's shift and scales base and margins by
the resolution; the clip is the port on the GPU path (engine c79d16f8, patch 0059, UXP_PIN moved). The daemon
passes the shift and sets the displayport again when the shift leaves the re-anchor margin or the zoom changes,
and sets the resolution BEFORE the displayport (a pinch from 2.33 to 2.8 clipped again; the zoom-out painted the
whole page, 160 tiles).
- A tap or a second double tap on a zoomed page found nothing: the GPU path keeps the position in the engine's
scroll and the compositor's shift, not the render pan docToViewport read. docOffset/gpuAlignForHitTest scroll
the engine to the card's position before a discrete hit test; a zoomed-in page with no block under a double tap
answers the page's width, which the adapter clamps to the fit.
- Above a resolution of ~4096 / the layout viewport's larger side the frame still clips (3.49) and empties (4.0),
even with the resolution held (JIHAD_GPU_RES_MAX, kept for bisection): the page's maximum-scale is capped at
4000 / the declared viewport's larger side on the GPU path (2.91 for desktop Wikipedia, 4.0 for a device-width
page). The cause, the coast gap of a flick (the engine paints only at the coast's end: ~0.5 s of blank lower card
and no fixed header) and the first frame at a new zoom (1.6-2.9 s) are T-527's.
Verified on the bench after the fixes, all three variants (dev copies: libxul 4d753b8f, daemon 7c29035a, the
originals kept as .orig): the double tap to the infobox at 2.33 full at rest, a sideways drag at the zoom painted,
pinches to 2.99 full, the zoom-out at once, a tap on a zoomed link opening it, Mojo's pinch on mobile Wikipedia
never grey, the WebGL cube through a pinch, a drag and three reopenings, the settings and personas pages flicked
to both ends. T-514 (the owner's finger round) closes with it; R12's "finds it smooth" stays the owner's word.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The owner's 2026-10-24 directive: drive the finger-blocked criteria with an emulated finger, not by
assumption. Read from the device and its binaries: hidd sends each scan of the Cypress panel as one
datagram of 16-byte input_events to the AF_UNIX datagram socket LunaSysMgr binds at
/var/run/hidd/TouchpanelEventSocket (libhal's halTouchpanelMain.module parses it: EV_FINGERID opens a
finger, EV_ABS 0/1 x/y, EV_KEY BTN_TOUCH down/up, EV_GESTURE the flick velocity, one EV_SYN a scan; the
Qt QPA plugin libqpalm.so makes QTouchEvents). tools/device/finger/finger.c sends the same datagrams
in the panel's frame from portrait screenshot coordinates (raw x = y, raw y = 767 - x).
Proven on the bench: libhal logs "Unexpected code" for a malformed datagram; a tap opens the app
menu; a 500 px drag scrolls jihad-long-static.html from paragraph 1 to 8 through the card, the adapter
and the daemon; a 160 ms flick coasts to paragraph 33; a pinch out zooms in; a long press opens the
context menu; a double tap on Wikipedia makes the daemon log smartZoom; a tap on the address field
raises the keyboard with the address selected and a tap on the q key types q.
The kit cavekit-finger-emulation.md (R1 the path, R2 the gestures, R3 the round, R4 fit), Tier 89
(T-523 the finger, T-524..T-526 the round by area, T-527 the GPU floor under the owner's levers:
sensord off and the compositor thread), docs/FINGER-TESTS.md's head, PICKUP's evening note. Codex hit
its usage limit on the investigation; the path was read in-house with radare2.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The chain ran without a prompt once commit.gpgsign was false per repo in Jihad-Browser and
third_party/uxp (the owner, 2026-10-24: Gitea credentials serve git.trueog.net / git.true-og.net
only). Engine 7a872e8f, then 506c4114 (T-487), 3f4e1a27 (T-513/T-499 with the engine fold),
318869f2 (Tier 88). PICKUP, the build site and the loop log carry the hashes and the owner's push
commands; T-521 is complete in the registry.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Tier 88 (context/plans/build-site.md): the registry had no unblocked task left; what remained to an
agent were criteria no task covered any more.
T-518. Criteria met by earlier tasks ticked with their evidence: gpu R11's review (T-513 and Codex's
2026-10-24 pass), content-blocking R7 (the bundle's sha check against NOTICE and the lists' README, the
README paragraph with cardview.jpg, round 26's licence reading), modern-web R1 (T-484 and run 2c; the
README's Caveats link) and T-500's two. T-501's stays [~] for MSE (below). The template placeholders
never filled (T500_KIT, T501_KIT, T501_DEVICE_RESULT in docs/MODERN-WEB.md, DEVICE_STATE_EVENING and
TRACE_PICKUP in PICKUP) are filled; .cavekit/config is the owner's one line again; the README's
"Where it stands" regenerated.
T-519. On the build with the review fixes (engine 0058), the 10 s scripted pan on six pages under
JIHAD_GPU_VERIFY=1 (docs/rounds/t519-2026-10-24/): 0 rows structurally wrong on all; 0 rows differ at
all on the long page, jihad-fixed-both.html (new: an opaque header AND footer, nothing else), sticky-mix,
scroll-js and Hacker News; Wikipedia 3 of 70 frames with resampling-class rows. The one miss is the
verify mode's own: on jihad-anim-css.html the animated block's 160 rows differ (the reference samples the
animation at its own compose time), every other row equal — R2's first criterion [~] with the engine
fix named. The dumps looked at frame by frame; Hacker News at 0.955 shows its right side and bottom.
R4's four ticked on the compositor's lines: the fixed-both page reads two bands with both kept, the long
page one kept, Wikipedia's see-through panels as 2 columns in 3-5 chunks; sticky-mix takes the whole path
as in T-503's rounds (its sticky layer changes every step). R3's table: the GPU path against NOGPU=1 on
every page, in docs/RENDERING-OPTIONS.md — the GPU path ahead on every page (long static 30-33 against 11-12.5 fps, Wikipedia 18.5-26 against 2.7-2.9, Hacker News 37-48 against 6-10). The frame rates were measured again
without verify after a first sweep read 5-6 fps under it (a whole reference per job):
tools/device/t502/README.md says so; pan-run.sh takes ROUND_DIR.
T-520. The blocker's device criteria on the Enyo card through the inject channel, the listed hosts mapped to the
host PC in the device's /etc/hosts for the run (only the blocker can then stop a load): a listed host opens
top-level; EasyPrivacy's ||123count.com^$third-party loads first-party (the page on that host) and is refused
third-party; an @@||host^ exception (a list copy with one added, exceptions 6+1083 against 5+1079) loads the
host; the master switch and hiding flip live through setpref (the observer re-applies the sheet); the
settings line shows the versions and a counter that rose 10 -> 16 on a page with 6 refusals; "Update the lists
now" moved the lists three times and the line followed; shouldLoad on 200 refused images is within 10 % of
blocking off (6.5 against 6.0 s, the floor the engine's own per-image work); MSE reports H.264 and AAC
(T-501's criterion closed). R4's cost criterion was NOT met: hiding on cost the MDN page's copy 3.7-7.5 s
against 2.6-3.0 off, the real cached page 15 against 11, and a daemon's first load 8-12 s.
T-522. Four variants of the cached sheet on the device named the cost: the 268 @-moz-document regexp groups
(one per site-excepted generic selector, each carrying the 160 generic-hide hosts' 2.4 KB regexp again),
evaluated per document; the same 13,079 keyed selectors under url-prefix scopes or bare cost nothing. The
compiler (version 11) keeps those selectors apart (excSel) and the component adds them to the per-document
sheet for every host the selector is not excepted on, with the generic-hide sites (hosts and address
prefixes) honoured there too; the global sheet keeps its one regexp group. Measured: the MDN copy with hiding on 2868, 3104, 2876 ms against 2712, 2578, 2846 off
(+8.7 %, within R4's 15 %), a daemon's first load 3.7 s where it took 8-12; generic hiding still applies
under a host name, a site-excepted selector is hidden where not excepted, and a $generichide host
(jetzt.de mapped to the host PC) gets neither; the compile reports 268 excepted selectors per document and
154 generic-hide sites with 4 address prefixes.
New test pages: jihad-fixed-both.html (an opaque header and footer), jihad-tp-first/third.html,
jihad-exception.html, jihad-hiding.html, jihad-excsel.html, jihad-mse-probe.html, jihad-plain-ok.png.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The kit's three criteria ticked with their evidence (the gate and the shipped daemons' three startup
lines, the 57 patches reproducing the pin, the docs' numbers); PICKUP's evening delta (T-505/T-511/T-517
blocked by the circuit breaker, the registry's cycle broken, detached builds); the README's progress
regenerated from the kits. Next: T-513, Tier 87's review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Engine 0055: the bands and the sticky columns' chunks of a pan frame are regions of one composite (the
transaction, the diff and BeginFrame/EndFrame once; per region its world, Prepare and RenderLayer),
where each was a composite of its own at ~5 ms of CPU. Engine 0056: a tile whose transformed bounds
miss the clip is skipped before its texture is locked and bound. Chunked desktop Wikipedia frames
compose in 10.6 ms where they took 22.5, and its 10 s scripted pan runs at 23.4-23.9 fps (20.0-21.7
before). Verify mode, the default checks of all three interfaces and the measurements are in
docs/rounds/t502-2026-10-17/floor-r314-320.txt (rounds 342-348), with what was tried and dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
Rounds 334-341 (docs/rounds/t502-2026-10-17/dtap-r334-341.txt): scripted double taps on Wikipedia,
2 of 18 first frames over 250 ms, with the second core unplugged after an idle pause; the scripted
tap skips the touch path's governor ramp, so a finger's tap is for the owner's round. T-504 stays
PARTIAL: its token budget (the plugin's default) is spent and is the owner's to raise.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
Kept like an opaque layer once composed alike three frames running with the content sliding under
it one colour, it applied on Wikipedia to one 13-row sliver only (the contents panel's highlight
follows the reading position) while its per-frame save and compare slowed frames (median gap 71-72 ms
against 42). Not kept; the records say what is left (fewer passes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
A shared frame segment holds four screens of pixels and a GPU frame is about one. A frame may now
start further into it: the daemon writes where (its window, in rows) into a 64-byte trailer at the
segment's end before every publish (render/browserserver/Src/JihadFrameWindow.h), and the adapter's
BrowserOffscreen::rasterBuffer() reads the frame from there. The adapter marks the segments it makes;
the daemon moves windows only in those (an adapter from before keeps every frame at the start). The
YAP messages and the BrowserOffscreenInfo header are unchanged.
A slot's frame built on its own older frame after a scroll is now that frame with its window moved by
the scroll (engine 0053: the shift is then only the kept rects), where 3.7 MB of rows were moved on
the compositor thread every pan frame; near an end of the segment the frame goes back to the middle
with one copy. Both lanes keep every slot's stale rows (the express lane used to drop the other slots'
records on each publish; a frame composed and dropped adds its change too, and a forgotten composite
drops them all), so the express lane can build on its slot's own frame; it takes that or the copy from
the newest slot by measured frame time (the other tried every 16th frame). A windowed GPU frame is
never the software path's repaint base.
Rounds 325-332: verify mode on main-lane pans 0 structurally wrong frames after the pan began;
dumped express frames coherent; the long test page pans at 30.5-36.4 fps (median frame gap 26 ms, was
29); Wikipedia unchanged at 19-21 (its sticky panels' passes); the default checks of all three
interfaces pass with the window-aware adapters, frames right by eye. JIHAD_GPU_NO_WINDOWS=1 and
JIHAD_GPU_EXPRESS_NO_OWN=1 for A/B.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
Rounds 314-320: band frames with the sticky panels' column chunks take 40-46 ms on the compositor
thread, those without 18-27; each composite pass costs ~5 ms of CPU whatever its area, and the two
pinned Wikipedia panels are see-through sticky layers composed again every frame in 3-4 chunks. Both
cores are full during a pan. The shift on a worker, the whole path and one main-thread notification a
frame were tried and reverted (no gain). pan-run.sh's PAN_TOP=1 samples the processes' and the
daemon's threads' CPU over 4 s of the pan.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
Engine 0051: on the GPU path WebGL asks EGL for its context and is not blocklisted there; the
Adreno 220's GL_INVALID_ENUM for the stencil back write mask no longer fails the context's init;
every pbuffer context on the GPU path is in one share group, so a canvas's texture is composited
with no readback (the driver has no EGLImage from a GL texture); a context is released from its
thread before it is destroyed (the second WebGL page closed faulted in libGLESv2 before).
JIHAD_GPU_NO_SHARE=1 keeps contexts unshared, read back through a canvas-sized pbuffer. On the
software path WebGL stays off.
Engine 0052 and the daemon: a compose job gives the columns its bands are stale in, the poll
reports the columns the composite changed, each slot keeps its stale columns as it keeps its rows,
and a narrow change's bands are read as one column (the compositor job 44 ms against 47 for a
480 px canvas; JIHAD_GPU_NO_COLUMN_READ=1 reads whole rows). The slot's own frame is the base when
its stale rows beyond the last three frames' changes are few: the 480 px canvas copied the whole
frame from the newest slot every frame to read the same rows.
The test page render/goanna/test/jihad-webgl-cube.html (a spinning textured cube; size, checks,
context loss, the missing extensions, a stop and a counter beside the canvas for the verify mode).
Rounds 281-313 (docs/rounds/t502-2026-10-17/webgl-r281-313.txt): contexts on the GPU path, none
on the software path; the cube renders right; 8 card open/close cycles and a lose/restore without
a fault; the spot checks pass, the four missing extensions listed; the column read verified against
whole composites with a change outside the canvas's columns. Open: the 480 px cube draws 21.5-25 fps
but the card gets 18-20.5 (240 px 26.5-27): the compositor job's floor, carried by T-517.
Also: adapter-install.sh no longer turns on the adapter's paint log (its gate filled /var and hung
the daemon in a journal commit); crash reports list every executable mapping; gpu-bench section 17
(no EGLImage from a GL texture) and the GL extension list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
The generated upstart jobs start each daemon with JIHAD_GPU_COMPOSITOR=1 JIHAD_GPU_EXPRESS=1. The
daemon settles the path before the engine starts (the engine tests the variable's presence in many
places): JIHAD_GPU_COMPOSITOR=0, or user_pref("jihad.gpu.enabled", false) in the profile's prefs.js
(read as text: the pref service is not up yet; the bundle's default is true), unsets both and the
software path renders. The log names the path and why ("rendering: ..."). A GPU that fails at run
time still falls back by itself (T-509).
On the bench (CE 3.1.0) the three jobs were installed in a remount window: Enyo, Mochi and Mojo each
load, pan, pinch and close desktop Wikipedia on their own default daemon through the card's
self-test hook (tools/device/t502/default-check.sh), no fault and no fallback; the pref put Mochi on
the software path and back, and JIHAD_GPU_COMPOSITOR=0 did the same for a hand-started daemon.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
The double taps' slow first frames were not draws: the engine timeline (engine 0050) put their time
in the first readback after a pause (100-390 ms, 30-40 usual) and the CPU swizzle (20-122 ms, 7
usual): the clocks after two idle seconds and the engine's other threads. The YAP reader thread now
raises the clock hold on a card's scroll, zoom or smart-zoom request (the clock hold made thread-safe;
JIHAD_CLOCK_HOLD_MS), and the Compositor and JihadExpress threads run at nice -6 (JIHAD_GPU_FRAME_NICE).
Double taps over 250 ms: 4-8 of 12 before, 0-2 after (median 158 ms). Left: the GPU's own ramp after
a pause. A warm-up frame on the smart-zoom request was tried and dropped (its own slow frame queued
ahead of the zoom's). The timeline also logs the clock hold's transitions and each express frame's
clocks.
T-510 (the default) is repointed from T-504 to T-515: it waited so the owner's zoom complaint would
not ship, and the express lane closed it; T-504 stays open for the ramp.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
With JIHAD_GPU_EXPRESS=1 on the GPU path each card's YAP commands are read on a thread of the
proxy's own and the daemon's frame bookkeeping is under one frame lock, which the main thread lets go
of only in the engine's pump and in a frame's layer paint. An express thread composes the layer tree
as it stands at the card's newest scroll and zoom and publishes the frame: every frame while the card
moves (the main lane paints layers and moves the anchor), and the main thread's stalls otherwise.
Engine 0048 splits the frame's paint from its compose post and lets any thread post and poll jobs;
0049 stops a composite acknowledged after the card's widget is gone from dereferencing it (a crash
on a card's disconnect seen in a device round).
Device rounds 240-269: zooms while the page's script holds the main thread come within 250 ms
(Wikipedia after its load 10/10 within 160-225 ms, held off median 681 and three never framed; a
page holding the main thread 600 ms in every 700 within 45-91 ms, held off median 450); that page
pans at 26-32 fps (2.4-3.8). Desktop Wikipedia pans at 19-21 fps: the compositor thread's ~45 ms a
frame is the floor now (T-517); the double taps' slow frames are GPU draws (T-516). Off by default
with the GPU path; no change with it off (the test page 30-33 fps, software 2.9-3.2).
Also: wants under a lock of their own (the reader thread never waits for the frame lock: a proxy's
teardown joins it), commands tagged in arrival order (a main frame serves the card once it took up
the newest), main frames older than an express publish dropped, the zoom hold released by an
express frame, the inject channel's pan and zoom through the card's path, JIHAD_GPU_TIMELINE,
JIHAD_GPU_ZOOM_DUMP=log, zoom-seq.sh, jihad-script-hold.html; a 32-bit 1L << 40 in the band merge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
On the device with the GPU path: VP8, Theora, VP9 and H.264 clips play to the end with every frame
decoded, the video its own image layer, at the clips' own rates (15 fps clips published at 16-20
fps; the 640x360 24 fps clip at 15.5-18, the software path 10-15); a 60 s clip matches the software
path frame for frame. Styled video (jihad-video-styled.html): rounded corners, an opacity fade and a
modal over the video draw as on the software path. Flash: the test movie animates (21-25 fps) and
takes keys (green to red).
H.264, AAC and MP3 had played on neither path: push-engine-update.sh never carried FFmpeg's
libavcodec/libavutil, so a device updated by it since a full install without them refused every
MP4. It carries them now (pushed to all three variants). The build site's T-499..T-501 and the
handoff's placeholders are filled from commit 4ded8d72 and today's checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
The GPU frame now carries 0.15 of the card's height above and below the view once a page has been
panned (JIHAD_GPU_OVERSCAN; engine patch 0046 widens the root scroll frame's clip by it), so the card
has content to pan into between frames and while the main thread is busy, as the software path's
frames have: the grey past a view-sized frame's edges was one of the owner's grey areas. The view
sits the same rows down every frame (a view row that moved with the frame broke the compositor's
row shift: every frame read whole), rows outside the page are filled white, and frames are capped at
the compositor's first pbuffer (1400 rows: a 1412-row frame read its top rows from the bottom ones,
flipped). Verify: 0 structurally wrong rows on the test page, sticky-mix, desktop Wikipedia and
Hacker News. It costs ~10% of the pan rate (a quarter of the height cost 15-18%).
Measured (scripted 10 s pans, rounds 191 and 200-201): the GPU path ahead of the software path on
every page (the test page 30-35 fps against 2.7-3.2, sticky-mix 17-19 against 2.4-3.1, desktop
Wikipedia 19-26 against 2.7-2.9, Hacker News 31-37 against 6.4-10.4; with overscan the test page
25-32 and Wikipedia 16.5-24). The gaps of 56 ms and more are the page's own restyle on its first
scroll (955 ms), the paints of the displayport moves (58-121 ms) and short page events; Wikipedia at
24 fps needs frames made off the main thread, now T-515.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9