Document runtime exclusions and verify offline transfer savings
This commit is contained in:
150
plans/manifest-hygiene-report.md
Normal file
150
plans/manifest-hygiene-report.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# 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:
|
||||
|
||||
```sh
|
||||
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.
|
||||
|
||||
```sh
|
||||
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):
|
||||
|
||||
```text
|
||||
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.
|
||||
Reference in New Issue
Block a user