# Phase 1 — per-file asset hashes and server manifest 1. Add tests first in `server/asset-manifest.test.js`, deliberately extend `server/static-delivery.test.js` and `frontend/shell-consistency.test.js`. 2. Describe the existing shell in `frontend/assets.json`: ordered file groups, contract 1, eager core/settings/playlist/service-mode/lyrics-editor. Preserve index script order and both service-worker sources byte-for-byte. 3. Add importable `server/asset-manifest.js`; integrate boot-time recursive hashing, stamped index, manifest endpoint, per-file headers and rollback into `server/server.js`. Register module tests in `server/package.json`. 4. Stamp HTML CSS/JS resource URLs. Keep font preloads consistent with fonts.css and retain the existing webmanifest URL. Retain plain JS-created worker/import URLs: introducing a new helper would require modifying the unchanged worker's shell or frontend callers. Inventory these exceptions explicitly in the report. Hash every recursively served file, including files outside the shell, so the banner never misses a change. 5. Extract `b77938a` using `perf/make-shell-fixture.sh` with an explicitly computed expected tag (never query production). Add `perf/migration.mjs` and instructions to `perf/README.md`; test old-client Refresh UI, a single banner, complete new cache, no loop, offline reload, and legacy URL bodies on Chromium and WebKit. Run reduced LTE cold/warm/offline baselines. 6. Review against master plan §3.1/§6b; commit `plans/phase1-report.md` (≤350 words) with commit IDs, measurements, exceptions and iPhone checks; write DONE01 to the queue signal only after verification succeeds. Hash self-reference: stamp resource URLs first; hash a canonical index with its build-meta placeholder intact to derive the manifest build tag. Stamp that tag and expose the actual final index byte hash. Canonical tag computation normalizes only this derived index field, avoiding a cryptographic fixed-point requirement. SW response hashing must likewise describe injected bytes rather than raw source. Rollback: `ASSET_HASHING=0` restores the existing recursive build tag and legacy single-tag CSS/JS stamping/header behavior. Default ON; test both modes. Risks: old-worker ignoreSearch, stalled installation, mutable stale URLs, recursive paths, derived HTML/SW hashes, shared iOS cache/audio quota. Acceptance: both unit suites green after each implementation commit; no weakened characterization tests; migration passes in both browsers; reduced baseline cold, warm and offline boot succeeds with no first-paint regression. Phase 1 still fetches the whole shell on update; <30 KB CSS-only updates belong to Phase 2.