# Phase 4 verification The served frontend is build `c3dbdd7039db`, source delivery commit `2c50035`. `phase4-final-2026-10-08.json` records parent b335f95 because the review fix was uncommitted when its scratch tree was copied; that tree includes exactly the frontend subsequently committed as 2c50035. The isolated cold result records both 2c50035 and the build tag. Earlier phase result files were restored after harnesses wrote their default paths; Phase 4 copies retain the actual new run evidence. ## Six green seams | Commit | Seam | Original bytes moved | Frontend tests | Server tests | Both-engine cached smoke / hidden timers | |---|---|---:|---:|---:|---| | 3c9a24a | Eager shell/views/section rail | 98,378 | 192 | 183 | Pass | | 5fb9564 | Presenter | 9,492 | 193 | 183 | Pass | | 8e7fa19 | Remote client | 30,421 | 194 | 183 | Pass | | 94ea211 | Watch party | 22,833 | 195 | 183 | Pass | | 0475a18 | Share/External | 13,334 | 196 | 183 | Pass | | b335f95 | Stats view | 7,074 | 197 | 183 | Pass | | 2c50035 | Lazy-entry rejection handling | — | 198 | 183 | Pass | After every seam, `node --test frontend/*.test.js`, `bun install` then `bun run test` in server, `node perf/hidden-timers.mjs`, and `node perf/seams.mjs --browser all` ran. Server tests used PATH="$PWD/../perf/.tmp/test-venv/bin:$PATH" so yt-dlp worker tests also ran, without skips. Logs are in gitignored perf/.tmp; representative actual output: ``` ℹ tests 198 ℹ pass 198 ℹ fail 0 ℹ skipped 0 ``` Server totals across its isolated test processes: 183 pass, 0 fail, 0 skip. ``` chromium {"browser":"chromium","checked":["shell","presenter","remote","party","share","stats"],"offlineNetworkRequests":0,"errors":[]} webkit {"browser":"webkit","checked":["shell","presenter","remote","party","share","stats"],"offlineNetworkRequests":0,"errors":[]} Result: ALL ASSERTIONS PASSED ``` The cache smoke stops Bun and blocks its proxy, then tests home playlists and eager Settings, Presenter blank/pair validation, Remote pair form/close, Party local UI, Share timestamp editing, External uploaded-media sheet/M3U, populated Stats view, and a rejected entry action. Network relays are still required to connect parties or remotes; offline verification proves cached code/UI, not offline relay service. Existing lazy harness passes offline layouts, first-use, Settings extensions, contract guards, pinned instances and update-during-playback on both engines. One review smoke WebKit process terminated during concurrent browser checks; an isolated rerun and the final expanded both-engine smoke passed. Initial harness fixture errors (incorrect Settings selector, absent VM helper, timestamp format, hidden list pane) were corrected to exercise actual UI; no product tests were removed or weakened. ## Measurements Actual commands: ``` node perf/baseline.mjs --runs 1 --browser all --profile lte --scenario all --out perf/results/phase4-final-2026-10-08.json --compare perf/results/after-phase0-2026-10-07.json node perf/baseline.mjs --runs 3 --browser chromium --profile lte --scenario cold --out perf/results/phase4-cold-isolated-2026-10-08.json --compare perf/results/after-phase0-2026-10-07.json ``` Full reduced run overlapped other checks; its Chromium long tasks were 495 ms. Repeat cold-only runs with no concurrent tests produced 297/274/275 ms (median 275). Keep both results: neither is rewritten. WebKit lacks the long-task API. Historical Phase 0 is five-run data; these reduced measurements are directional. | Metric | Phase 0 Chromium / WebKit | Phase 3 Chromium / WebKit | Phase 4 Chromium / WebKit | |---|---:|---:|---:| | Initial compressed JS/CSS | 276,414 / 338,940* | 221,307 / 274,813 | 210,419 / 255,200 | | Cold boot ms | 10,480 / 3,290 | 7,193 / 3,293 | 7,010** / 2,977 | | Cold FCP ms | 1,572 / 1,100 | 1,356 / 2,467 | 1,352** / 2,401 | | Cold long-task ms | 454 / unavailable | 287 / unavailable | 275** / unavailable | | Warm boot ms | 692 / 139 | 419 / 81 | 467 / 132 | | Offline boot ms | 259 / 137 | 166 / 84 | 180 / 90 | | Core app update wire bytes | 611,338 / 680,822 | 159,905 / 194,026 | 125,795 / 147,259 | | Inactive CSS blocking-readiness bytes | 611,476 / 680,831 | 29,724 / 28,082 | 30,263 / 28,713 | | Feature edit through actual idle cache fill | — | — | 33,408 / 41,364 | *Matched Phase 0 source reconstructed in Phase 3; historical JSON lacks initial bytes. **Isolated three-run Chromium cold median. Other Phase 4 numbers are the one-run both-browser suite. Warm counters include unfinished initial idle caching. CSS readiness excludes its later background payload; it is not full update cost. WebKit first paint still fails the master no-regression target. Full five-run comparisons belong to Phase 5. Native iPhone timing/audio/quota is unverified. Actual feature-update output: ``` run 1/1: 33408 bytes, 8 reqs, dur=1376ms run 1/1: 41364 bytes, 7 reqs, dur=1358ms ``` Only presenter.js application payload transfers (3,118 / 3,587 compressed bytes). Totals include changed stamped index, manifest, version checks, sw.js checks and WebKit's 1,954-byte sync-core worker import. No app.js, unchanged core views, fonts, icons or CSS transfer. The benchmark asserts this rather than inferring it from the lower total. Final app.js is 408,473 versus 587,029 bytes: −178,556, −30.4169%; original code moved totals 181,532, facade overhead 2,976 bytes. ## Migration, rollback and owner checks Both engines pass b77938a migration with lossy/stalling and unthrottled proxies: 122 cached SHELL entries, one banner and exactly one user-initiated reload, 21 legacy URL checks, N-1 open-tab continuity, offline boot, playback guard, eviction repair and incremental refresh. Result JSON records the real runs. No automatic activation/reload or live singleton replacement is added. Rollback ASSET_SYNC=0 and ASSET_HASHING=0 use the existing legacy path; commands and results are in perf/README.md and perf/results. No new feature flag. Revert an individual seam to restore its previous parsing behavior. Owner must verify a real iPhone PWA: warm feature first-use, selected/offline layout, Presenter/Remote/Party sessions, update guard/override, locked audio 30 minutes, interruption/resume, saved OPFS music surviving cache quota pressure. Nothing was pushed, merged or deployed.