Files
ytplayer/plans/manifest-hygiene-report.md

7.7 KiB
Raw Blame History

Runtime manifest hygiene

Based on main/offline-fix tip 0b8e398; offline-fix was already merged when this worktree was created. Implementation: dd09f66. No frontend runtime, loading, service-worker, playback or layout code changed.

Policy and delivery

server/asset-manifest.js now selects runtime inputs separately from delivery metadata. It excludes *.test.js, *.test.mjs, *.md, *.txt, *.c, *.entry.js, /admin.html, and all files beneath a perf/ directory. The optional assets.json include array accepts exact root-relative existing file paths to override this policy. An explicitly declared group cannot silently lose an excluded dependency: boot rejects it unless the path is included.

The 49 excluded files here are 44 unit tests, admin HTML, the Plus Jakarta Sans font licence, two WASM C build sources, and the Framework7 source build entry. WASM binaries, fonts, icons, workers and the Framework7 runtime bundle remain.

Excluded files remain directly servable, with the existing content hash, ETag, compression and cache rules: plain requests revalidate (font-folder requests keep their 30-day cache rule), matching ?v is immutable, and stale ?v is no-store in per-file mode. Delivery hashes are computed separately; they never enter manifest.files or build identity. ASSET_HASHING=0 retains single-tag stamping and its legacy header matrix, but its tag also uses only runtime inputs. ASSET_SYNC=0 still selects the legacy worker.

Fresh installs cache only runtime assets. On an incremental upgrade, excluded entries from the previous manifest may survive for one build under existing N-1 retention, then are pruned as N-2. No new excluded entries are downloaded. This preserves retention for already-open pages instead of changing worker pruning or contracts/pinning.

Inventory

Measured from the untouched before snapshot and the fixed frontend using the respective real server manifest modules. Counts include stamped index and SW.

Metric Before After Reduction
Manifest files 151 102 49 (32.5%)
Summed manifest file sizes 2,044,337 B 1,807,059 B 237,278 B
JSON manifest body 15,601 B 12,551 B 3,050 B

Excluded source bodies total 237,278 B, or 64,694 B using per-file Brotli compression. Actual install/update measurements below include protocol and manifest/index effects; compression estimates are not substituted for those runs. Full inventory: perf/results/hygiene-assets.json.

Verification

Commands:

node --test frontend/*.test.js
cd server && bun install && bun run test
node perf/offline-complete.mjs --browser all --conditions --out perf/results/hygiene-offline.json
node perf/migration.mjs --browser all --profile lossy --out perf/results/hygiene-migration.json
node perf/offline-complete.mjs --browser all --legacy --out perf/results/hygiene-rollback-offline.json

The server test command used a worktree-local Python venv on PATH with yt-dlp installed so both environment-sensitive worker tests ran successfully. Frontend: 209 passed, zero failures. Server: 186 passed, zero failures. Manifest/static/shell tests cover excluded-file identity invariance in both hash modes, app.js identity changes, explicit inclusion, group validation, direct bodies/hash/header behavior, SPA delivery and rollback flags.

Offline completeness: Chromium and WebKit each report manifest count 102, missing [], failures [], across all four layouts and lazy features. Page closure/interrupted completion, worker restart/re-registration, Save-Data, two tabs and online-event resumption pass. Linux WebKit uses same-context worker re-registration because persistent CacheStorage restart is broken there; its offline proxy blocks transport because native setOffline breaks cached navigation. Native OPFS-dependent video-edit behavior cannot be exercised in Linux WebKit.

Lossy legacy migration passes on both engines: one banner, one explicit-user reload, offline launch, playback guard, legacy URLs, eviction repair and incremental refresh. WebKit records four tolerated offline API transport errors, with the offline assertions passing. Migration caches include retained N-1 entries, so their raw key count is deliberately greater than manifest count.

Rollback limitation found during extra verification

An additional --legacy offline-completeness run disables both hashing and sync. It fails on both the untouched base and fixed build: the legacy SHELL omits piano-engine.mjs, so its direct offline request returns 503 across layouts. The legacy shell also intentionally does not cache its own sw.js; the harness therefore marks that inventory entry missing too, without a corresponding offline UI failure. Before: 151 manifest entries / 51 missing; after: 102 / 2 missing. The other 49 before-only misses are precisely the excluded non-runtime files. Normal sync-mode completeness is 102/102 with no failures. Header/injection rollback tests pass; this change neither fixes nor worsens the existing legacy piano coverage gap. Results: hygiene-before-rollback-offline.json and hygiene-rollback-offline.json.

Reduced performance measurements

Three runs per scenario/engine, LTE. Before used an isolated untouched snapshot of 0b8e398 inside this worktree; after used the fixed server. No external worktree was modified. Cold wire totals include page requests and install/completion fetches; these are not claimed to be worker-only bytes. Chromium uses CDP LTE throttling; WebKit uses the LTE proxy.

node perf/.tmp/before/perf/baseline.mjs --runs 3 --browser all --profile lte --scenario all --out perf/results/hygiene-before.json
node perf/baseline.mjs --runs 3 --browser all --profile lte --scenario all --out perf/results/hygiene-after.json

All values below are actual medians; bytes are wire bytes.

Engine Scenario Before B After B Before ms After ms
chromium Cold visit + install 728,266 660,390 7101 7090
chromium Warm reload 8,132 8,159 647 445
chromium Offline reload 0 0 285 209
chromium Core app.js edit 126,358 125,729 1008 753
chromium theme-glass.css edit 44,027 43,434 1600 1372
chromium Presenter edit + idle fill 42,065 41,436 1392 1376
webkit Cold visit + install 1,189,813 1,111,241 2321 2310
webkit Warm reload 0 0 92 89
webkit Offline reload 0 0 100 88
webkit Core app.js edit 148,709 147,955 2013 2013
webkit theme-glass.css edit 57,732 56,976 1479 1466
webkit Presenter edit + idle fill 55,382 54,625 1494 1557

Cold requests fall 165 → 116 (Chromium) and 209 → 160 (WebKit), exactly 49 fewer each. Cold wire savings are 67,876 B (9.3%) and 78,572 B (6.6%). Ordinary runtime updates already shared unchanged files before this fix, so their savings are mainly smaller manifest metadata: approximately 0.6–0.8 KB. Test-only edits now leave the tag unchanged and trigger no build update. Milliseconds for cold/warm/offline are boot-done; update milliseconds are completion duration. These three-run timing differences are descriptive, not a performance claim. Warm Chromium API traffic varies independently of the asset set.

Real output excerpts (after):

run 3/3... done (660390 bytes, 116 reqs, boot=7090ms)
run 3/3... done (1111241 bytes, 160 reqs, boot=2310ms)
run 3/3... done (147955 bytes, 5 reqs, dur=2014ms)
run 3/3... done (56976 bytes, 9 reqs, dur=1483ms)

Real iPhone checks

Confirm installed-PWA offline launch and feature/layout first use after update; completion after suspension, storage pressure/OPFS sharing, and old excluded cache entries disappearing after the subsequent build. Linux automation cannot prove iOS worker lifetime, quota eviction or native OPFS behavior.