From 5763504a57d6b7aee0c89362bfd564dbe08cd81d Mon Sep 17 00:00:00 2001 From: Jozkah <38704522+Jozkah@users.noreply.github.com> Date: Wed, 4 Nov 2026 12:01:55 +0000 Subject: [PATCH 01/38] perf: findings, fix plan and phone-emulated perf probe Refs CNCKitchen/ScanRuler#91 Co-Authored-By: Claude Opus 5.5 --- .../plans/2026-11-04-mobile-perf-fixes.md | 300 ++++++++++++++++++ .../specs/2026-11-04-mobile-perf-findings.md | 140 ++++++++ scripts/perf-probe.mjs | 268 ++++++++++++++++ 3 files changed, 708 insertions(+) create mode 100644 docs/superpowers/plans/2026-11-04-mobile-perf-fixes.md create mode 100644 docs/superpowers/specs/2026-11-04-mobile-perf-findings.md create mode 100644 scripts/perf-probe.mjs diff --git a/docs/superpowers/plans/2026-11-04-mobile-perf-fixes.md b/docs/superpowers/plans/2026-11-04-mobile-perf-fixes.md new file mode 100644 index 00000000..7242b0e3 --- /dev/null +++ b/docs/superpowers/plans/2026-11-04-mobile-perf-fixes.md @@ -0,0 +1,300 @@ +# Mobile UI Responsiveness Fixes — Implementation Plan (rev 2, after review) + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Every tab and toggle paints its UI in the first frame after the tap on a phone, and no toggle pays shader compiles, scan-sized uploads or new WebGL contexts it does not need. + +**Architecture:** Fix the measurement first (a project with real content, profiler-off timing, medians), then ship the evidence-backed wins (TopBar fold, unchanged-map skips, section-view compile, keep-alive viewports, BVH refit). Work whose payoff the first baseline could not show (overlay rebuilds, deferral, workers, React restructuring) is gated: each such task starts by adding its probe row and proceeds only if the row shows the cost. Battery and startup items come last. + +**Tech Stack:** React 19.2, zustand 5, three.js 0.180, three-mesh-bvh 0.8.3, Vite 6, Vitest 3, puppeteer-core e2e. + +**Spec:** `docs/superpowers/specs/2026-11-04-mobile-perf-findings.md` (F1–F20, baseline, and the *Review corrections* section — read it; several findings were downgraded). + +## Global Constraints + +- Every source file keeps `// SPDX-License-Identifier: AGPL-3.0-only`; new files get it too. +- Match the codebase's comment style: full-sentence comments explaining *why*, in the existing voice. +- No new runtime dependencies. +- Colours on screen after the tap must equal colours before the change (byte-identical buffers), except for one transient frame where Task 9 says so. +- Documented trade-offs that a task reverses are listed under **Owner decisions** and are not implemented until the owner agrees. +- The commands/agent API awaits `nextFrame()` (`src/commands/core/common.ts:162`) to see the stage follow the stores; anything deferred must be flushed by it. +- Vitest: `npx vitest run --maxWorkers=8`. Typecheck: `npx tsc --noEmit`. Never two heavy builds/e2e at once. +- e2e: `npx vite --port 5391 --strictPort` in the background, then `APP_URL=http://localhost:5391/ node scripts/.mjs`. `e2e:` in this plan means `scripts/e2e-.mjs` (or `plugins/*/scripts/e2e-.mjs` for `reverse`, `mesh`), alias or not. +- Before any PR: `npm run build` must pass; state its output. + +## Owner decisions (ask before implementing) + +1. **Keep split view and 2D viewer WebGL contexts alive after first use** (reverses `App.tsx:1671-1673`). Recommended: yes — measured 1 context + 6.8 MB + 1 program per split open. Task 6. +2. **Keep the `deflect` attribute while in the deviation workspace** (+12 B/vertex; reverses `SceneManager.ts:2855-2859`). Recommended: yes. Task 10. +3. **Cap pixel ratio on touch screens** (sharpness change; `(pointer: coarse)` also matches touch laptops). Recommended: not as a perf fix — offer it as a setting. Not in this plan. + +## Review Focus + +1. Hidden tab: rAF does not fire; deferred work must complete and `nextFrame()` must flush it *after* its own frame resolves (Task 9 test). +2. A field buffer recomputed in place (same `Float32Array`) must not be served from a cache — caches key on store versions (Tasks 2, 3 tests). +3. Rapid repeated taps (map off→on within one frame, tab A→B→A): end state = last tap, painted once (Task 9 test). +4. Scan replaced / crease changed while a worker job or kept-alive viewport is pending: stale result dropped, never shown on the new geometry (Tasks 6, 10, 11 tests). +5. Projects with many elements, dimensions and sections, and scans ≥ 1M triangles — every gated task measures on the rich scenario, not only on the bare sample (Task 0). + +--- + +## Phase A — Measure properly + +### Task 0: Probe v2 + +**Files:** Modify `scripts/perf-probe.mjs` (add to git in this task). + +**Interfaces (produced):** +- `PERF_RUNS` (default 5): each measured tap is repeated (toggle taps alternate on/off) and the **median** INP and long-task max reported. Profiler **off** for these runs; one extra profiled run per tap supplies the hot list. +- Drop the "UI appears" column (its t0 includes CDP overhead). +- `budget: { links?, uploadMB?, contexts?, inpMs? }` per measure; `PERF_ASSERT=1` exits 1 with a `BUDGET FAIL: