Document incremental sync verification and performance gaps

This commit is contained in:
Jonathan Sykes
2026-10-08 00:21:19 +08:00
parent 92404563b3
commit 4950d3e417
7 changed files with 2500 additions and 0 deletions

View File

@@ -181,3 +181,34 @@ which receive hashed URLs as HTML scripts. Font preloads retain plain URLs to ma
`fonts.css`; the webmanifest and its icon URLs also stay plain and precached. `fonts.css`; the webmanifest and its icon URLs also stay plain and precached.
`piano-engine.mjs` is manifested but is historically absent from SHELL; this `piano-engine.mjs` is manifested but is historically absent from SHELL; this
existing offline piano limitation is deferred to the loader phase. existing offline piano limitation is deferred to the loader phase.
## Phase 2 incremental-sync verification
The current migration harness verifies the production page's own ready fast path
into `ytplayer-assets`: no `__ytpfresh` downloads, no reload before Refresh UI,
one banner/reload, an open N-1 tab, playback guard, incremental refresh after
an eviction, and offline launch. It preserves the legacy user-triggered reload
exception. Async CacheStorage/registration conditions are polled with awaited
`page.evaluate`; Playwright's `waitForFunction` does not poll async predicates.
```bash
node perf/migration.mjs --browser all --profile lossy
node perf/migration.mjs --browser all --profile unthrottled
node perf/baseline.mjs --runs 1 --browser all --profile lte --scenario all \
--out perf/results/phase2-final-2026-10-07.json \
--compare perf/results/after-phase0-2026-10-07.json
ASSET_SYNC=0 node perf/baseline.mjs --runs 1 --browser all --profile lte \
--scenario offline --out perf/results/phase2-rollback-2026-10-07.json
ASSET_HASHING=0 node perf/baseline.mjs --runs 1 --browser all --profile lte \
--scenario offline --out perf/results/phase2-hashing-rollback-2026-10-07.json
```
Results now include response paths/statuses and compressed response-body bytes
for cold/update runs. The total includes service-worker script checks/imports;
inspect individual responses to distinguish those from app asset downloads.
Migration results are `phase2-migration-<profile>-2026-10-07.json`.
`ASSET_SYNC=0` selects the legacy path while preserving the persistent utility
cache. `ASSET_HASHING=0` also selects legacy sync because single-tag URLs cannot
pass per-file hash checks. Current assets.json groups all block; idle loading and
lazy parsing await Phase 3. Incremental mode now caches the manifested
`piano-engine.mjs` even though it remains absent from the legacy SHELL.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,131 @@
{
"date": "2026-10-07T16:00:49.668Z",
"commit": "bb446c717f92ef912f5000ddc757b9d1071120dc",
"runsConfigured": 1,
"browsers": {
"chromium": {
"lte": {
"offline": {
"requestCount": {
"median": 0,
"min": 0,
"max": 0,
"values": [
0
]
},
"wireBytes": {
"median": 0,
"min": 0,
"max": 0,
"values": [
0
]
},
"fcp": {
"median": 76,
"min": 76,
"max": 76,
"values": [
76
]
},
"lcp": {
"median": 300,
"min": 300,
"max": 300,
"values": [
300
]
},
"domContentLoaded": {
"median": 233,
"min": 233,
"max": 233,
"values": [
233
]
},
"bootDone": {
"median": 279,
"min": 279,
"max": 279,
"values": [
279
]
},
"reloadDuration": {
"median": 306,
"min": 306,
"max": 306,
"values": [
306
]
}
}
}
},
"webkit": {
"lte": {
"offline": {
"requestCount": {
"median": 0,
"min": 0,
"max": 0,
"values": [
0
]
},
"wireBytes": {
"median": 0,
"min": 0,
"max": 0,
"values": [
0
]
},
"fcp": {
"median": 129,
"min": 129,
"max": 129,
"values": [
129
]
},
"lcp": {
"median": 158,
"min": 158,
"max": 158,
"values": [
158
]
},
"domContentLoaded": {
"median": 117,
"min": 117,
"max": 117,
"values": [
117
]
},
"bootDone": {
"median": 153,
"min": 153,
"max": 153,
"values": [
153
]
},
"reloadDuration": {
"median": 165,
"min": 165,
"max": 165,
"values": [
165
]
}
}
}
}
}
}

View File

@@ -0,0 +1,41 @@
{
"commit": "b77938a",
"results": [
{
"browser": "chromium",
"oldTag": "1175f1a1d2c1",
"newTag": "4f7090fff60c",
"banners": 1,
"updateReloads": 1,
"cachedFiles": 92,
"durationMs": 27514,
"wireBytes": 622009,
"offline": true,
"legacyURLs": 21,
"profile": "lossy",
"userInitiatedOnly": true,
"playbackGuard": true,
"evictionRepair": true,
"incrementalRefresh": true,
"offlineTransportErrors": 0
},
{
"browser": "webkit",
"oldTag": "1175f1a1d2c1",
"newTag": "4f7090fff60c",
"banners": 1,
"updateReloads": 1,
"cachedFiles": 92,
"durationMs": 19558,
"wireBytes": 731221,
"offline": true,
"legacyURLs": 21,
"profile": "lossy",
"userInitiatedOnly": true,
"playbackGuard": true,
"evictionRepair": true,
"incrementalRefresh": true,
"offlineTransportErrors": 4
}
]
}

View File

@@ -0,0 +1,41 @@
{
"commit": "b77938a",
"results": [
{
"browser": "chromium",
"oldTag": "1175f1a1d2c1",
"newTag": "4f7090fff60c",
"banners": 1,
"updateReloads": 1,
"cachedFiles": 92,
"durationMs": 10811,
"wireBytes": 630657,
"offline": true,
"legacyURLs": 21,
"profile": "unthrottled",
"userInitiatedOnly": true,
"playbackGuard": true,
"evictionRepair": true,
"incrementalRefresh": true,
"offlineTransportErrors": 0
},
{
"browser": "webkit",
"oldTag": "1175f1a1d2c1",
"newTag": "4f7090fff60c",
"banners": 1,
"updateReloads": 1,
"cachedFiles": 92,
"durationMs": 9283,
"wireBytes": 744401,
"offline": true,
"legacyURLs": 21,
"profile": "unthrottled",
"userInitiatedOnly": true,
"playbackGuard": true,
"evictionRepair": true,
"incrementalRefresh": true,
"offlineTransportErrors": 4
}
]
}

View File

@@ -0,0 +1,131 @@
{
"date": "2026-10-07T15:55:07.011Z",
"commit": "54a07a0a2ae1711e441545b208c45c55b3318963",
"runsConfigured": 1,
"browsers": {
"chromium": {
"lte": {
"offline": {
"requestCount": {
"median": 0,
"min": 0,
"max": 0,
"values": [
0
]
},
"wireBytes": {
"median": 0,
"min": 0,
"max": 0,
"values": [
0
]
},
"fcp": {
"median": 72,
"min": 72,
"max": 72,
"values": [
72
]
},
"lcp": {
"median": 72,
"min": 72,
"max": 72,
"values": [
72
]
},
"domContentLoaded": {
"median": 225,
"min": 225,
"max": 225,
"values": [
225
]
},
"bootDone": {
"median": 249,
"min": 249,
"max": 249,
"values": [
249
]
},
"reloadDuration": {
"median": 262,
"min": 262,
"max": 262,
"values": [
262
]
}
}
}
},
"webkit": {
"lte": {
"offline": {
"requestCount": {
"median": 0,
"min": 0,
"max": 0,
"values": [
0
]
},
"wireBytes": {
"median": 0,
"min": 0,
"max": 0,
"values": [
0
]
},
"fcp": {
"median": 109,
"min": 109,
"max": 109,
"values": [
109
]
},
"lcp": {
"median": 138,
"min": 138,
"max": 138,
"values": [
138
]
},
"domContentLoaded": {
"median": 101,
"min": 101,
"max": 101,
"values": [
101
]
},
"bootDone": {
"median": 133,
"min": 133,
"max": 133,
"values": [
133
]
},
"reloadDuration": {
"median": 145,
"min": 145,
"max": 145,
"values": [
145
]
}
}
}
}
}
}

43
plans/phase2-report.md Normal file
View File

@@ -0,0 +1,43 @@
# Phase 2 report
Shipped on `codex/phase2`: plan `a1f16f2`; incremental sync/playback guards
`54a07a0`; atomic state, exact hash rejection and retention `5b25cca`;
single-tag rollback compatibility `bb446c7`; migration verification `5d1e5ec`;
fail-closed status handling `9240456`.
Persistent `ytplayer-assets` stores verified exact URLs and current/previous
manifests. Downloads resume, use six workers/three attempts, and publish only
complete blocking sets. Activation retains N-1 changed/removed files, prunes
N-2, then deletes legacy caches; quota is logged. All current groups block;
lazy parsing/background warming await Phase 3.
Server gaps addressed: inject ASSET_SYNC and the hashed sync-core import,
hash final worker bytes, compress manifest responses while preserving build
ETags. ASSET_SYNC=0 selects legacy sync; ASSET_HASHING=0 also selects legacy.
Verification: 168 frontend/181 server tests pass without skips. Chromium/WebKit
migration passes with and without stalls using actual b77938a code: ready:true,
zero legacy refresh downloads, one banner/user-triggered reload, N-1 open tab,
eviction repair, incremental refresh and offline boot. Both rollback flags
pass offline browser checks. Async polling fixes the Playwright harness race.
Accepted legacy exception: b77938a retains its four-second timer after the USER
presses Refresh UI and does not report playback. New pages guard playback,
offer explicit override and post PLAYING; workers defer known-playing requests
and never call skipWaiting during installation.
Final one-run LTE measurements (`phase2-final-2026-10-07.json`, Chromium/WebKit):
JS updates 155,648/189,354 bytes versus 611,338/680,822; CSS 31,476/30,545 versus
611,476/680,831. Sync fetches changed app/style + index/manifest; fonts/icons transfer
zero bytes. Browser worker checks add overhead: Chromium downloads sw.js twice;
WebKit also downloads the unchanged imported core. Thus total CSS exceeds
30,000 bytes.
Cold bytes: 624,762/1,191,130 versus 1,040,158/1,180,749. Chromium transfers app.js
once; WebKit transfers it twice despite HTTP-cache reuse attempts. Cache sharing
remains unresolved. Cold boot: 11,155/4,327 ms versus 10,480/3,290; warm:
671/121 versus 692/139; offline: 264/133 versus 259/137. Single-run cold regressions
need further investigation; no claim that every performance target was met.
Owner iPhone checks: locked playback, override/cancel, stall recovery, offline
launch and OPFS/quota coexistence. No push, merge or deployment.