← the Atlas

OpenCode v2 Perf — What Kolu Can Adapt

analysis · budding ·implemented ·

OpenCode Desktop v2's "10×" is a streaming-markdown number. Mapped onto Kolu's actual surfaces, most of it has no consumer — but every technique that does has now shipped — R1 Pierre 1.2.10 + Shiki 4.2.0 (#1360), R2 the highlight worker pool (#1363), and R3+R4 the canvas-gesture p99 harness + rAF-coalesced pan/zoom.

A read of OpenCode Desktop v2’s perf release (≈4 → 45.7 FPS under a 30× CPU throttle) against Kolu’s real architecture. Method: 13 agents — external research ▸ code-map ▸ synthesize ▸ adversarially verify; every load-bearing fact re-checked against source + the npm registry.

The 10× is workload-specific

OpenCode’s six techniques: (1) migrate the message timeline to TanStack Virtual with chat anchoring (anchorTo:'end', followOnAppend); (2) flicker / scroll-jump fixes (overflow-anchor:none + a manual rAF visual-anchor, and an absolute — not delta — scrollbar-thumb mapping); (3) bump Pierre 1.2.10 + Shiki 4.2.0; (4) Shiki → Web Worker (stream-tokenize the new suffix, reply with compact [content, style] tuples); (5) append-only assumptions (a text-delta accumulator bug that was the bogus 4-FPS baseline, plus freeze-all- prior-messages-only-the-last-mutates); (6) perf-trace CPU work — profile under 30× throttle, optimize p95/p99 frame time, and log rejected experiments (their token-batching and height-estimator ideas regressed the tail).

The headline number is a dev-profiling figure on a long, growing list of variable-height markdown/diff messages. That list is the thing being virtualized, anchored, and worker-highlighted — it’s the whole game.

Why most of it has no consumer in Kolu

Kolu’s perf-relevant surfaces, colored by verdict — green = done / already covered / N-A, teal = the live agent stream:

shiki 4.2.0 ✓ #1360@pierre/diffs 1.2.10 ✓ R1 #1360solid-pierre CodeView · R2 ✓ #1363client + transcript-html viewssolid-markdown · already cachedterminal · xterm.js (live stream)canvas pan/zoom · R4 ✓ coalescedgesture p99 harness · R3 ✓ 2.3× parser + workerproved the freezelarge-diff render
Kolu's perf surfaces, all wins now green. R1 (#1360) + R2 (#1363) the diff path; R3+R4 the canvas-gesture harness + rAF-coalesce (#1368). Teal = the live agent stream (no consumer for the streaming-list techniques).

What transfers

AdaptationImpactEffortStatus
R1Upgrade @pierre/diffs 1.2.1 → 1.2.10 (+ Shiki 4.2.0)highmediumshipped · #1360
R2Pierre worker pool + shiki-js engine for the diff pathhighmediumshipped · #1363
R3CPU-throttle gesture work harness (per-event burst)mediummediumshipped · #1368
R4rAF-coalesce the canvas wheel pan/zoom write-stormmediumlowshipped · #1368

Low R1 — Bump @pierre/diffs to 1.2.10 + Shiki 4.2.0 — SHIPPED (#1360)

Done #1360 . ^1.2.1 → ^1.2.10 across solid-pierre, client, transcript-html; shiki ^3.23.0 → ^4.2.0 in solid-markdown (single shared copy). Deps-only — no source code changed. The flagged breaking-change risks were all non-issues: fileGap is used nowhere (the fileGap→spacing rename never bit), pierreTheme.ts was already on the --*-override convention, and the PIERRE_DIFFS_LINE_HEIGHT=16 virtualizer contract (#1026) held — proven green by typecheck, solid-markdown/solid-pierre unit tests, and nix build. Brings the ≈2.3× faster parsePatchFiles and the worker substrate R2 needs. (pnpmDeps hash refreshed; pierre/SKILL.md pin still reads 1.2.1 — a stray to fix.)

Low R2 — Pierre worker pool + shiki-js, for the diff path only — SHIPPED (#1363)

Done #1363 . The 1.2.10 type defs settled the open question: the engine is a CodeView option (preferredHighlighter: 'shiki-js', in CODE_VIEW_DIFF_OPTION_KEYS), and worker offload is the CodeView constructor’s 2nd arg (workerManager), built once via getOrCreateWorkerPoolSingleton. So solid-pierre now owns a session-lived worker-pool singleton (workerPool.ts) and hands it to every CodeView; plain ASTs paint synchronously while highlighted tokens stream back off-thread. Kolu supplies the workerFactory (new Worker(new URL('@pierre/diffs/worker/worker.js', import.meta.url), { type: 'module' })), so the worker bundles through the client’s Vite — which needed worker.format: 'es' since Pierre’s worker code-splits its Shiki grammars (the default iife can’t). Browser-only (created in onMount), so the SSR transcript-html path never spawns a Worker. Pool size 2; never torn down. The transitive @shikijs/transformers@3 duplicate (a @shikijs/[email protected] beside the 4.x engine) remains Pierre’s choice — worth watching if it regresses.

Low R3 — CPU-throttle gesture-work harness — SHIPPED (#1368)

Done #1368 . Shipped as scripts/gesture-p99/ (dependency-free CDP over Node’s built-in WebSocket). It drives the real canvas and gates R4. Two findings turned the methodology: (1) a headless/CDP Chrome’s rAF isn’t vsync-capped — it fires ~1:1 with input dispatch (verified: gesture intents == rAF flushes, headless and headful-under-xvfb), so an rAF-paced fling can’t show coalescing there; and (2) Chrome 143 dropped HeadlessExperimental.beginFrame, so manual frame-clocking is out. The harness therefore measures the thing R4 changes directly — per-event main-thread work — via a synchronous burst of K WheelEvents, immune to the frame scheduler. Write-up: canvas-gesture-p99.md.

Medium R4 — rAF-coalesce the canvas wheel write-storm — SHIPPED (#1368)

Done #1368 . OpenCode’s real principle — per-frame work proportional to new data — applied to Kolu’s actual hot loop. useCanvasViewport.ts now accumulates the frame’s pan delta (sum) and zoom factor (product toward the last anchor) and applies them once per requestAnimationFrame instead of per raw wheel event. Feel-neutral for the cases that actually occur: a pure-pan or pure-zoom frame lands on the exact per-frame state the per-event path reached (applyGestureBatch telescopes the math, clamping per event). A mixed pan+zoom frame — only possible when a pointer-drag pan overlaps a ctrl+wheel zoom, since a wheel event is pan xor zoom — uses a canonical zoom-then-pan order: a deliberate, bounded, non-accumulating approximation rather than re-walking the per-event list (which is the work R4 deletes). All three cases are pinned in transforms.test.ts, since canvas gestures have no e2e coverage. #1308 deferred this as latent/benign; R3 confirmed the deferral’s exact condition. On a 16-tile canvas under throttle, a 60-event zoom burst went from an 865 ms main-thread freeze (≈52 dropped 60 Hz frames) at 6× → 1.3 ms, with tile writes down 60× (2,880 → 48 — the coalescing ratio). gestures.ts (ownership / preventDefault) stays synchronous and untouched; only the state-write defers.

Sequencing: R1 ✓ → R2 ✓ → R3 ✓ (the harness gated R4) → R4 ✓ (measured, not guessed).

Confirmed facts & the resolved question

The verification pass settled every load-bearing claim — and R1 (#1360) has since moved the version facts forward: