Author SHA1 Message Date
Codex 90213d7f62 Research safe CPU memory and kernel measurements 2026-09-25 23:34:54 +05:30
5 changed files with 139 additions and 1229 deletions
+139
View File
@@ -0,0 +1,139 @@
# Safe CPU, memory, and kernel measurements for Odin
Research for [Establish safe CPU, memory, and kernel measurements](https://git.bongbetic.com/xavierk/odin/issues/2), part of [Find the way to Odin’s build-ready specification](https://git.bongbetic.com/xavierk/odin/issues/1).
**Accessed:** 25 September 2026. **Status:** recommendations for later decisions, not a selected workload suite. No benchmarks, stress tests, privilege changes, or host configuration changes were performed.
## Findings that shape the decision
Odin can reuse established workloads and Linux interfaces, but no single tool measures system usability, peak performance, and hardware health. Keep four outcomes distinct:
- **Performance measurement:** completed work per second or elapsed time under a specified workload.
- **Responsiveness:** foreground request or wakeup latency, including its tail, under a specified competing load.
- **Pressure observation:** time lost to CPU, memory, or I/O contention during that interval.
- **Health finding:** a detected verification failure or reported hardware error, with the observation's coverage.
This separation follows the tools' actual contracts: sysbench benchmarks operations; schbench measures artificial requests and scheduling delays; PSI measures stalls; memtester checks memory contents. Stress-ng explicitly says it was **not intended as a precise benchmark suite**. Successful stress completion establishes only that the chosen work completed without detected failures under those conditions. [sysbench][sysbench] [schbench][schbench] [PSI][psi] [memtester][memtester-man] [stress-ng][stress-readme]
## Candidate comparison
Maintenance observations describe inspected releases or repository activity, not a guarantee of future support. GPL notices and third-party dependencies need checking again for the exact distributable artifacts. Perf is distributed in the Linux source tree, whose COPYING specifies GPL-2.0-only with the syscall exception and notes that other licenses may also apply; preserve the selected tool and dependency notices. [Linux COPYING][kernel-license]
| Candidate | Useful measurement and limitations | Controls, output, portability, maintenance |
|---|---|---|
| **sysbench CPU / memory** | CPU events are a small prime-search workload, not a general application-performance model. The memory test defaults to a **1 KiB block**; its `100G` default is cumulative transfer size, not RAM allocation. A default run therefore cannot stand for DRAM bandwidth. | Thread, event, duration, warmup and percentile controls; human-readable built-in reports require a pinned parser. Upstream advertises x86_64 and aarch64 packages. GPL-2.0-or-later. Latest published release inspected: 1.0.20, April 2020; repository push activity March 2025. Void still packages 1.0.20. [README][sysbench] [CPU source][sysbench-cpu] [memory source][sysbench-memory] [release][sysbench-release] [metadata][sysbench-meta] [Void][void-sysbench] |
| **schbench** | Reports synthetic request latency, wakeup latency, and requests/second. Closer to responsiveness than peak throughput. Its server-inspired matrix workload deliberately penalizes preemption using per-CPU locks; this is not a desktop interaction model. | Runtime, worker/message counts, work size, request rate, affinity and JSON percentiles. Source has x86 and aarch64 paths; Linux pthread/futex interfaces, no normal root requirement found. Musl behavior remains unverified. GPLv2. Inspected upstream commit `6300b8f`, June 2025. [methodology][schbench] [source][schbench-source] |
| **stress-ng** | Appropriate candidate for controlled background load and selected verification diagnostics. Bogo operations are unsuitable as Odin's cross-test score foundation. | Explicit byte/worker/time caps, verification, YAML and differentiated exit codes. Upstream documents musl builds and testing on ARM64/x86-64. GPL-2.0-or-later; release 0.22.01, September 2026. Void's recipe has explicit musl handling. [README][stress-readme] [manual][stress-man] [release][stress-release] [Void][void-stress] |
| **STREAM** | Established sustainable memory-bandwidth kernels: Copy, Scale, Add, Triad. No memory-latency or comprehensive error-detection result. Each array must exceed cache requirements; a small cache-resident run is not a compliant STREAM result. | Portable C; multicore uses OpenMP and its runtime. Array size is a build parameter in reference 5.10. Text output and numerical validation. Reference source dates to 2013: stable method, not evidence of a modern portability test matrix. Custom license permits use/redistribution but imposes result-naming/run-rule conditions. [source and license][stream-source] [run rules][stream-rules] |
| **lmbench `lat_mem_rd`** | Pointer-chain latency over sizes/strides exposes cache, memory and TLB behavior. Its manual acknowledges vulnerability to stride-sensitive prefetchers; do not present this as an architecture-independent “true RAM latency.” | Warmup, repetitions, bounded size, text pairs. GPLv2 COPYING inspected; Intel's repository is active but contains old documentation. Portability and selected-file licensing need validation before adoption; not a recommended mandatory dependency yet. [manual][lmbench-memory] [README][lmbench-readme] [COPYING][lmbench-license] [metadata][lmbench-meta] |
| **cyclictest / perf** | Cyclictest measures timer wakeup latency; perf supplies diagnostic counters. Neither is a substitute for foreground application response. | Cyclictest offers duration, histogram and JSON; source is GPL-2.0-only, current rt-tests release 2.11. Its startup tests permission to enter SCHED_FIFO even when ordinary policy is selected. Perf depends on kernel/PMU access and can emit JSON. Treat both as optional diagnostic coverage. [cyclictest][cyclictest] [privilege check][rt-utils] [rt-tests release][rt-release] [perf][perf-stat] |
| **memtester** | Online checking of allocated memory, not all installed RAM. Failures can involve memory, CPU, temperature or power; the result does not identify a replaceable DIMM by itself. | Byte size and finite iteration count; default iterations are infinite. Text plus exit-bit mask. Record actual allocation and locking, not only exit status. GPL-2.0-only. Version 4.7.1's December 2024 fix addresses stricter C23/GCC 15 compilation; Void recipe inspected still selects 4.6.0. [manual][memtester-man] [source][memtester-source] [changelog][memtester-changelog] [Void][void-memtester] |
| **Memtest86+** | Offline, bootable diagnostics reach almost all memory without the resident OS. They cannot run as an ordinary in-terminal stage. | GPLv2. Stable v8.10, May 2026, lists x86, x86-64 and LoongArch64. Current main additionally lists AArch64 with UEFI boot. This is a **stable/development difference**, not certified aarch64 coverage. [stable README][memtest-stable] [development README][memtest-main] [release][memtest-release] |
| **EDAC / rasdaemon** | Hardware-error telemetry, complementary to active tests. Availability depends on hardware, firmware, drivers and exposed events. | Read EDAC counters where accessible; optionally consume an existing rasdaemon history. Rasdaemon monitors kernel trace events and has database backends; its repository shows September 2026 activity and GPLv2 metadata. Starting it is a separate privileged monitoring action, not necessary to read available counters. [EDAC ABI][edac-abi] [rasdaemon][rasdaemon] [metadata][ras-meta] |
## Measuring usability under contention
**Recommendation:** evaluate paired idle and loaded foreground-request measurements as a first-class candidate. Run the same bounded foreground work alone, with a fixed CPU background load, and with separately controlled memory pressure. Preserve normal scheduling policy, work size, thread count, placement, requested arrival rate, actual throughput, sample count, and latency distribution. Report absolute latency and degradation relative to idle; a fast idle result can coexist with poor responsiveness under contention.
Existing tools can supply the observations. Schbench records both request and wakeup latency, supports a fixed request rate, and can coexist with an independently bounded stress-ng background worker. Sysbench's rate-limited engine is another candidate: its source adds measured queue time to event duration and reports queue length. These remain synthetic proxies for foreground work; neither measures keyboard-to-pixel delay, terminal rendering, browser interaction, or application launch by itself. Those need their own workload definitions. [schbench methodology][schbench] [schbench source][schbench-source] [sysbench queue][sysbench-core] [sysbench timer][sysbench-timer]
Schbench needs qualification before selection. Its README says warmup defaults to five seconds, while the inspected source defaults to zero and bypasses its warmup reset in request-rate mode. It uses `gettimeofday`, so clock adjustments can contaminate timing. Set options explicitly, retain the revision, validate timing conditions, and decide whether its deliberate preemption penalty fits Odin's goal. Do not adapt work size independently on every system and then compare the resulting latency as equal work. [source][schbench-source]
Cyclictest is a useful separate scheduler diagnostic. Its default behavior can hold `/dev/cpu_dma_latency` at zero and suppress deep idle states; `--default-system` avoids that tuning. The source's unconditional real-time privilege check prevents assuming that an ordinary-policy configuration is universally unprivileged. Its timer latency, especially under SCHED_FIFO, is a different measurement from a normal foreground application's response. [manual][cyclictest] [source][cyclictest-source] [privileges][rt-utils]
## Kernel telemetry and compatibility
**PSI:** Read system and, where available, workload-cgroup `cpu`, `memory`, and `io` pressure. `some` is time when at least some tasks are stalled; `full` is time when all non-idle tasks are stalled together. The cumulative `total` counter permits interval deltas; rolling 10/60/300-second averages can smear a short benchmark across adjacent phases. **System-wide CPU `full` is undefined and exposed as zero for compatibility**—zero there cannot mean perfect responsiveness. PSI exists in the inspected Linux 4.20 source, but requires `CONFIG_PSI` and may be disabled by default pending `psi=1`. Probe actual files and readability, not only kernel version. [PSI][psi] [4.20 documentation][psi-420] [Kconfig][kconfig]
**Capacity and pressure:** Record usable RAM, `MemAvailable`, swap capacity/usage, process or cgroup memory, major faults, and swap/reclaim activity where exposed. `MemAvailable` is an estimate of memory available without swapping, not an allocation guarantee. Swap occupancy alone does not establish current pressure. `/proc/stat` supplies CPU time and steal time, but kernel documentation explicitly warns that `iowait` is unreliable. Correlate these observations with latency and PSI; do not derive a definitive bottleneck from CPU utilization or a single counter. [proc documentation][proc]
**Cgroups:** Observe effective CPU affinity/cpuset, CPU quota, memory/swap limits and relevant ancestors. Host RAM and online CPU counts may exceed what the benchmark is allowed to use. Cgroup v2 `cpu.stat` records throttling; `memory.events` separates high-limit reclaim, OOM conditions and kills. Parse by key: the kernel explicitly allows new `memory.stat` entries in the middle. These interfaces are independent of a particular init system, but writable delegation and enabled controllers are not guaranteed on Void, deb, rpm, containers, or user sessions. [cgroup v2][cgroup]
**Perf:** Treat hardware counters as enrichment. `CONFIG_PERF_EVENTS`, CPU PMU support, virtualization, `perf_event_paranoid`, capabilities and distribution policy can limit access. Kernel documentation recommends `CAP_PERFMON` over broad `CAP_SYS_ADMIN`; Odin should describe missing access rather than lowering system security settings. Record event identity and time-running percentage when multiplexing occurs. PMU-specific cache and pipeline events are not universal normalized scores. Perf's manual also warns of overhead at short sampling intervals, particularly below 100 ms. [security][perf-security] [Kconfig][kconfig] [perf stat][perf-stat]
**Architecture/libc:** Proc/sysfs/cgroup interfaces offer the strongest common layer across x86_64/aarch64 and glibc/musl. Workload binaries still need distinct, verified artifacts and recorded toolchain flags. Upstream stress-ng explicitly documents musl; sysbench's advertised architectures and Void recipes are useful evidence, but none of the inspected material certifies Odin's whole four-way architecture/libc matrix. STREAM additionally needs a compatible OpenMP runtime; rt-tests has library dependencies. A package recipe proves availability intent, not successful operation. Missing checks should carry reasons such as unsupported, permission denied, unavailable dependency, or insufficient safe resources. [stress-ng][stress-readme] [sysbench][sysbench] [Void recipes][void-sysbench] [STREAM][stream-source] [rt-tests Makefile][rt-makefile]
## Resource safety and cancellation
The following is a proposed execution contract, with exact caps left to the safety decision:
1. Calculate a conservative working budget from current `MemAvailable`, effective cgroup/ancestor headroom, expected tool/runtime overhead, and a retained reserve. Recheck while running. There is no sourced universal percentage that guarantees safety when other programs allocate concurrently.
2. Where delegated cgroup v2 control exists, put disposable workers in their own subtree and keep the supervisor outside that subtree. `memory.high` induces reclaim/throttling and **is not a hard cap**; `memory.max` bounds charged memory and can invoke OOM inside the cgroup. `memory.swap.max` separately controls swap. Use deliberate limits and record them because they change results. Caps reduce risk; they cannot guarantee that an unrelated system-wide shortage never kills a process. [cgroup v2][cgroup]
3. Reserve intentional pressure for explicitly selected, isolated work. Without reliable containment or enough reserve, recommend pressure **observation** and small bounded workloads, and report unavailable active-pressure coverage. Allocation success alone is insufficient: memtester's own manual warns about overcommit, swapping and OOM affecting other programs. [memtester][memtester-man]
4. Never expose memtester's physical-address/device modes in the normal benchmark path. They overwrite the mapped region and can crash the system when it belongs to another process or the kernel. For ordinary allocations, verify locked bytes and completed patterns/iterations. Linux permits unprivileged locking up to `RLIMIT_MEMLOCK`; larger locking requires suitable privilege, commonly `CAP_IPC_LOCK`. The tool's “run as root” advice should not force the entire TUI to run as root. [manual][memtester-man] [Linux mlock][mlock]
5. Use finite work/time limits and a supervisor deadline. For stress-ng, enable only reviewed stressors, verification where supported, and no OOM respawn (`--oomable`). Its `--oom-avoid` is a heuristic with measurement overhead, not containment. In 0.22.01, `--vm-bytes` describes a total across VM workers; other stressors have different allocation semantics, so retain the exact version and options. [stress-ng manual][stress-man]
6. Cancel the worker process group gracefully, then terminate remaining descendants after a defined grace period; use `cgroup.kill` when accessible. Preserve a cancelled/partial result. Stress-ng documents SIGINT cleanup, but its timeout can overrun during uninterruptible calls or cleanup. No userspace deadline guarantees immediate cancellation of an uninterruptible kernel task. [manual][stress-man] [cgroup kill][cgroup]
## Repetition, kernel settings, and TUI overhead
**Recommendation:** record warmup separately, repeat bounded measurements, retain all repetitions and dispersion, and report a median only at a clearly defined level. STREAM's official report takes the **best** iteration after discarding the first; a median of repeated STREAM run results is a different statistic. Do not silently relabel its internal minimum as a median, combine raw milliseconds with MB/s, or replace an unavailable result with zero. Overall score normalization belongs to the scoring decision. [STREAM source][stream-source]
Record kernel/build identity, visible preemption/scheduler settings, CPU topology and allowed CPUs, NUMA placement, THP policy, libc, workload/compiler version and flags, governor/driver, boost, power source and temperature observations. NUMA placement and THP policy affect what memory workload is actually measured. Kernel CPUFreq documentation explains that `scaling_cur_freq` can be a requested state rather than measured frequency, and boost depends on thermal/power conditions and package load. A governor name or falling frequency alone does not prove thermal throttling. Correlate sustained performance with available temperatures, thermal trip/cooling states, and power/frequency evidence; unavailable sensors remain unavailable. [CPUFreq][cpufreq] [NUMA][numa] [THP][thp] [thermal interfaces][thermal]
For “single core,” specify whether the worker is pinned and how the core is chosen on heterogeneous CPUs. For “multicore,” specify workers relative to allowed CPUs, SMT and quota; do not silently change those rules between systems. Preserve the machine's existing configuration for the baseline. Potential governor, scheduler, THP, affinity or kernel changes should be advice or separately labelled experiments, not automatic optimization before measuring.
The TUI competes for CPU time, memory bandwidth, cache and terminal I/O. Recommend throttled graph updates, buffered logs, no expensive animation during timed sections, and a quiet measurement mode that preserves cancellation. Avoid hiding this by reserving a core without recording it: that reduces tested capacity. Later validation should compare quiet versus normal rendering on the slowest supported machines and establish an overhead budget. This is a proposed qualification experiment, not evidence that a particular redraw rate is already safe. Lmbench explicitly warns about competing cache/CPU work; PSI's own Kconfig notes overhead can show up in synthetic scheduler stress tests. [lmbench][lmbench-readme] [Kconfig][kconfig]
## Memory errors, VM coverage, and remaining decisions
Online memtester cannot touch RAM occupied by the kernel or other processes. It may allocate less than requested and may continue unlocked; inspected 4.7.1 source can then still exit zero if its pattern checks succeed. Parse allocation/locking evidence alongside exit bits and report “no errors detected in the tested allocation,” with size, iterations and duration. A mismatch warrants investigation, not an automatic RAM-replacement diagnosis. [manual][memtester-man] [source][memtester-source]
EDAC counters reset at driver initialization or explicit reset; preserve counter baselines and `seconds_since_reset` without resetting them. Corrected errors merit attention, but uncorrected errors may cause a panic before a counter increments. DIMM labels can depend on board-specific userspace mapping. Therefore missing EDAC nodes, zero observed deltas, and an empty rasdaemon history cannot certify error-free RAM. Offer offline follow-up where supported; decide how development-only AArch64 Memtest86+ support should be presented. [EDAC ABI][edac-abi] [EDAC model][edac] [Memtest86+ stable][memtest-stable] [development][memtest-main]
VMs can validate packaging, libc/architecture execution, permissions, telemetry fallbacks, cgroup containment and result handling. Guest CPU/memory scores describe the guest allocation and host scheduling conditions; steal time is useful context. They do not certify the host's DIMMs, ECC pipeline, cooling, physical memory-channel bandwidth, or representative bare-metal scheduler tails. Rasdaemon's upstream QEMU tests intentionally inject virtual nonfatal events: useful for exercising decoding, not proving physical hardware health. [proc][proc] [rasdaemon CI description][rasdaemon]
**Recommended next decision:** shortlist sysbench for a narrow CPU baseline, STREAM for bandwidth, schbench for responsiveness qualification, selected stress-ng workers for bounded load, and memtester plus available EDAC/RAS for diagnostics. Keep perf/cyclictest optional; defer mandatory memory-latency scoring until a candidate is validated. Final selection remains open.
The human-facing decisions still needed are the foreground workload's meaning; fixed versus relative background load; inclusion of responsiveness in the median score; safe resource reserves and privileges; repetition/time allocation within the 10–20 minute standard run; treatment of heterogeneous cores and missing coverage; and whether offline/development-tool guidance belongs in the first release.
**Evidence limits:** no candidate was built or executed across the target matrix. Context7 resolved Linux kernel, sysbench, memtester and rt-tests documentation. Stress-ng, STREAM and schbench searches returned unrelated libraries, so no false library match was used; their owning sources were inspected directly. Memtester's upstream HTTPS site failed certificate validation; the report uses the original source/manpage/changelog preserved by Debian, cross-checked against the Void 4.6.0 source archive checksum, and identifies the version difference. Exact artifact compatibility, parser contracts, resource budgets, and score repeatability require later qualification.
[sysbench]: https://github.com/akopytov/sysbench/blob/master/README.md
[sysbench-cpu]: https://github.com/akopytov/sysbench/blob/master/src/tests/cpu/sb_cpu.c
[sysbench-memory]: https://github.com/akopytov/sysbench/blob/master/src/tests/memory/sb_memory.c
[sysbench-core]: https://github.com/akopytov/sysbench/blob/master/src/sysbench.c
[sysbench-timer]: https://github.com/akopytov/sysbench/blob/master/src/sb_timer.h
[sysbench-release]: https://github.com/akopytov/sysbench/releases/tag/1.0.20
[sysbench-meta]: https://api.github.com/repos/akopytov/sysbench
[void-sysbench]: https://github.com/void-linux/void-packages/blob/master/srcpkgs/sysbench/template
[schbench]: https://kernel.googlesource.com/pub/scm/linux/kernel/git/mason/schbench/+/6300b8f3a8922c61ea6bb2cdfa1901a42c0cc6fc/README.md
[schbench-source]: https://kernel.googlesource.com/pub/scm/linux/kernel/git/mason/schbench/+/6300b8f3a8922c61ea6bb2cdfa1901a42c0cc6fc/schbench.c
[stress-readme]: https://github.com/ColinIanKing/stress-ng/blob/V0.22.01/README.md
[stress-man]: https://github.com/ColinIanKing/stress-ng/blob/V0.22.01/stress-ng.1
[stress-release]: https://github.com/ColinIanKing/stress-ng/releases/tag/V0.22.01
[void-stress]: https://github.com/void-linux/void-packages/blob/master/srcpkgs/stress-ng/template
[stream-source]: https://www.cs.virginia.edu/stream/FTP/Code/stream.c
[stream-rules]: https://www.cs.virginia.edu/stream/ref.html
[lmbench-memory]: https://github.com/intel/lmbench/blob/master/doc/lat_mem_rd.8
[lmbench-readme]: https://github.com/intel/lmbench/blob/master/README
[lmbench-license]: https://github.com/intel/lmbench/blob/master/COPYING
[lmbench-meta]: https://api.github.com/repos/intel/lmbench
[cyclictest]: https://kernel.googlesource.com/pub/scm/utils/rt-tests/rt-tests/+/62da2befac98f811af8e56f2b7992fb09faa33d6/src/cyclictest/cyclictest.8
[cyclictest-source]: https://kernel.googlesource.com/pub/scm/utils/rt-tests/rt-tests/+/62da2befac98f811af8e56f2b7992fb09faa33d6/src/cyclictest/cyclictest.c
[rt-utils]: https://kernel.googlesource.com/pub/scm/utils/rt-tests/rt-tests/+/62da2befac98f811af8e56f2b7992fb09faa33d6/src/lib/rt-utils.c
[rt-release]: https://kernel.googlesource.com/pub/scm/utils/rt-tests/rt-tests/+/62da2befac98f811af8e56f2b7992fb09faa33d6
[rt-makefile]: https://kernel.googlesource.com/pub/scm/utils/rt-tests/rt-tests/+/62da2befac98f811af8e56f2b7992fb09faa33d6/Makefile
[perf-stat]: https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf-stat.txt
[kernel-license]: https://github.com/torvalds/linux/blob/master/COPYING
[perf-security]: https://docs.kernel.org/admin-guide/perf-security.html
[memtester-man]: https://sources.debian.org/data/main/m/memtester/4.7.1-1/memtester.8
[memtester-source]: https://sources.debian.org/data/main/m/memtester/4.7.1-1/memtester.c
[memtester-changelog]: https://sources.debian.org/data/main/m/memtester/4.7.1-1/CHANGELOG
[void-memtester]: https://github.com/void-linux/void-packages/blob/master/srcpkgs/memtester/template
[mlock]: https://man7.org/linux/man-pages/man2/mlock.2.html
[memtest-stable]: https://github.com/memtest86plus/memtest86plus/blob/v8.10/README.md
[memtest-main]: https://github.com/memtest86plus/memtest86plus/blob/main/README.md
[memtest-release]: https://github.com/memtest86plus/memtest86plus/releases/tag/v8.10
[edac-abi]: https://github.com/torvalds/linux/blob/master/Documentation/ABI/testing/sysfs-devices-edac
[edac]: https://docs.kernel.org/driver-api/edac.html
[rasdaemon]: https://github.com/mchehab/rasdaemon/blob/master/README.rst
[ras-meta]: https://api.github.com/repos/mchehab/rasdaemon
[psi]: https://docs.kernel.org/accounting/psi.html
[psi-420]: https://github.com/torvalds/linux/blob/v4.20/Documentation/accounting/psi.txt
[kconfig]: https://github.com/torvalds/linux/blob/master/init/Kconfig
[proc]: https://docs.kernel.org/filesystems/proc.html
[cgroup]: https://docs.kernel.org/admin-guide/cgroup-v2.html
[cpufreq]: https://docs.kernel.org/admin-guide/pm/cpufreq.html
[numa]: https://docs.kernel.org/admin-guide/mm/numa_memory_policy.html
[thp]: https://docs.kernel.org/admin-guide/mm/transhuge.html
[thermal]: https://docs.kernel.org/driver-api/thermal/sysfs-api.html
@@ -1,38 +0,0 @@
# Speedometer 3.1 pinned pack: redistribution audit
## Decision
**Do not redistribute the complete pinned archive yet.** The top-level BSD-style license permits redistribution of Speedometer's own work when its notice, conditions, and disclaimer travel with it. It does not establish rights for every embedded work. The pinned archive contains identifiable third-party material whose grant or compliance path is not yet established. This is a source audit, not a legal opinion or a benchmark run.
Source: [WebKit/Speedometer commit `1386415be8fef2f6b6bbdbe1828872471c5d802a`](https://github.com/WebKit/Speedometer/tree/1386415be8fef2f6b6bbdbe1828872471c5d802a), [root license](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/LICENSE).
## Exact inventory
The companion [per-file manifest](speedometer-license-manifest.tsv) records every relative path, byte count, SHA-256, and **review group** in the full GitHub commit archive: **1,118 files; 61,022,359 uncompressed bytes**. Its SHA-256 is `78edcd45649128fbd4c8659c1d054a2b9bb76a184b58ce59b37895c19f2bdfb6`. The archive download SHA-256 was `cfefa818d7789ed2f2b9f3f1bf608cc35e4e8241489eef47049296ce92791313`. Review groups flag provenance work; they are **not license determinations**. The complete source archive is a conservative candidate pack, not a selected minimum runtime file set.
Reproduce the manifest from that commit's GitHub `.tar.gz`: strip its single leading directory; sort regular-file paths by UTF-8 path; for each file write `path`, decimal byte count, lowercase SHA-256, and review group as tab-separated fields. Group rules: exact notice names and `*.LICENSE.txt`/`3rdpartylicenses.txt` first; then Gutenberg HTML, chart datasets, all news-site files, Adobe icon SVGs, then TodoMVC, React Stockcharts, charts, editors, and remainder in that order. The manifest itself is not an upstream artifact.
Nine separate notice files appear in the archive: `LICENSE`; `resources/todomvc/license.md`; `resources/react-stockcharts/build/static/js/2.8e539c84.chunk.js.LICENSE.txt`; Angular and Angular Complex `dist/3rdpartylicenses.txt`; and React, React Complex, React Redux, React Redux Complex `dist/app.bundle.js.LICENSE.txt`. Exact paths and hashes are in the manifest. Inline notices in JavaScript, CSS, HTML, SVG, and source maps also need preservation. A filename scan cannot prove all component notices were extracted.
## Established obligations and specific gaps
| Material | Evidence and current finding | Required action before shipping |
| --- | --- | --- |
| Speedometer-owned source | [Root license](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/LICENSE) requires retention of copyright, conditions, and disclaimer for source; reproduction in documentation or other materials for binary form. | Include root `LICENSE` in pack and distribution materials; keep original headers. |
| TodoMVC implementations | [TodoMVC subtree license](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/license.md) says MIT **unless otherwise specified**. Generated React and Angular notices identify further components. | Carry subtree license and all bundled notices. Audit each runtime bundle against its dependency versions; fill missing texts/attributions. |
| Angular bundles | Both [`3rdpartylicenses.txt` files](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/architecture-examples/angular/dist/3rdpartylicenses.txt) include MIT, Apache-2.0, and CC-BY-4.0 material. | Keep both files with their matching bundles; map each named component to bundle; satisfy Apache notice/change rules and CC attribution requirements as applicable. |
| React Stockcharts | Its [README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/react-stockcharts/README.md) claims MIT and points to upstream; [bundle notice](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/react-stockcharts/build/static/js/2.8e539c84.chunk.js.LICENSE.txt) lists dependencies. | Include upstream MIT text plus bundled notice; verify the actual bundled versions. |
| News-site template and CSS | Both [Next README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/newssite/news-next/README.md) and [Nuxt README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/newssite/news-nuxt/README.md) credit [`flashdesignory/news-site-template`](https://github.com/flashdesignory/news-site-template). Its repository has no visible license file, and [its package metadata](https://github.com/flashdesignory/news-site-template/blob/main/package.json) declares none. Pinned [news-site-css metadata](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/newssite/news-site-css/package.json) says ISC but does not provide that license text. The source and exported `dist` include adapted template material. **No redistribution grant established for the template.** | Get written permission or a verifiable applicable license from its rights holder, or remove/replace the NewsSite suites and all dependent files. Obtain and carry correct ISC text/notice for news-site-css. Reassess suite set and scoring if suites removed. |
| Chart datasets | [Dataset README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/charts/datasets/README) identifies two CSVs copied from an older D3 path. It does not state their data source or rights. `airports.csv`, `flights-airports.csv`, and the README are the three manifest paths. Generated `resources/charts/dist/assets/flights-airports-9a9e6422.js` embeds data. | Trace dataset origin and grant, then include required attribution. Otherwise replace with licensed/synthetic data and rebuild the affected chart assets, or omit that workload. |
| Adobe Spectrum icons and CSS | [Big DOM README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/big-dom-generator/README.md) says static shell uses Adobe `@spectrum-css`. Its source contains 22 `Smock_*.svg` icons plus three other SVGs, with no per-file notice. Generated `dist` and complex TodoMVC pages may embed this material. TodoMVC's MIT default alone cannot establish rights over Adobe assets. | Identify exact Adobe package/source and applicable icon/CSS license, preserve its notice, and verify built copies. If unavailable, replace assets and rebuild/verify affected pages. |
| Editor text | [`longtext.html`](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/editors/assets/longtext.html) is Project Gutenberg eBook 2650 per [asset README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/editors/assets/README.md). It includes Gutenberg branding and full license. [Gutenberg terms](https://www.gutenberg.org/policy/license) impose access, notice, format, fee, and territorial conditions while branding remains. | Preserve full embedded license and prominent notice; assess distribution geography and price. Simpler path: replace with separately cleared text of equivalent workload shape. |
| Other generated bundles and imagery | Charts, Editors, NewsSite, TodoMVC, React Stockcharts, images, maps, and CSS are generated or embedded works. Package lock entries identify dependencies but do not themselves provide all license texts; absence of a notice file is not proof of permission. | Build a component-to-file bill of materials from pinned locks/source maps and licenses. Review all shipped binaries/images individually; acquire missing grants or exclude/rebuild. |
## Pack gate
1. Define exact runtime file set; leave development files out only after proving every enabled suite resolves locally. Record each shipped file in a final manifest, with source commit and byte hash. The full-archive manifest here remains comparison baseline.
2. Map every final file to originating project or generated bundle components. Record SPDX identifier, copyright holder, evidence URL, required notice text, and fulfillment location. Mark unknown explicitly; no implicit root-license inheritance for third-party work.
3. Resolve the specific gaps above. Carry all nine existing notice files when their associated files ship; carry missing upstream notices and keep inline notices. Build a top-level `THIRD_PARTY_NOTICES` index with bundled texts or direct accompanying files.
4. Recheck after any exclusion, replacement, or rebuild. Any changed byte needs a new manifest digest and runtime validation. License clearance and offline functional validation are separate gates.
The earlier [offline pack research](speedometer-offline-pack.md) established static-path plausibility and proposed blocked-network validation; it did not clear redistribution rights. No benchmark, browser installation, or host change was made here.
File diff suppressed because it is too large Load Diff
-33
View File
@@ -1,33 +0,0 @@
# Speedometer 3.1 offline pack at the pinned upstream commit
## Decision-ready finding
The pinned [WebKit/Speedometer commit `1386415be8fef2f6b6bbdbe1828872471c5d802a`](https://github.com/WebKit/Speedometer/commit/1386415be8fef2f6b6bbdbe1828872471c5d802a) is a plausible source for an Odin **versioned browser workload** served from localhost. It contains built static applications and the benchmark runner. Its page identifies itself as Speedometer **3.1**, although `package.json` still says `3.0.0-alpha`; identify the pack by the full commit and a content digest, not that package version. The [about page](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/about.html) says workloads are built as static files and cannot depend on server infrastructure. The [runner](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/benchmark-runner.mjs) loads each suite in an iframe under `resources/`.
This is a **conditional yes** for an offline pack. Source inspection and static link checks support completeness, but they do not prove that a browser makes no external requests or that every workload succeeds without internet. Require a blocked-network browser smoke test before calling the pack offline-ready. This research did not execute a benchmark or install a browser.
## Pack contents and static checks
The [suite list](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/tests.mjs) declares 32 suites, 20 enabled by default. Local inspection of the pinned source archive found every declared suite entry path. The archive contained 1,118 files totaling 61,022,359 uncompressed bytes. For the root page plus the 20 enabled suite entry pages, a static HTML parser checked 194 `script`, asset `link`, and `img` references: none was external or missing. These numbers describe the checked archive, not a run result. The parser did not resolve dynamic JavaScript imports, CSS URLs, route requests, or user navigation.
The [main page](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/index.html) loads local CSS and `resources/main.mjs`; the [runner](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/benchmark-runner.mjs) constructs `resources/${suite.url}`. The Perf Dashboard workload deserves special attention: its [static page](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/perf.webkit.org/public/v3/index.html) replaces its API method with `mockAPIs()` and fetches 13 specified local JSON paths. All 13 files exist in the pinned archive. The ordinary [dashboard remote API](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/perf.webkit.org/public/v3/remote.js) supports XHR, so verify the mock remains active in the actual packaged page.
No `npm install` is needed to serve the already built workload assets. Upstream [development instructions](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/Development.md) use `http-server` for local development. Odin can serve the frozen file tree with its own loopback-only static server; do not rebuild application assets as part of a benchmark run. Use an HTTP origin rather than `file://`, since the suite uses modules, iframe paths, and fetches. The [upstream test harness](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/tests/run.mjs) is Selenium based and requires an installed browser and matching driver, per [Testing.md](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/Testing.md); it is not a prerequisite for serving the built pack.
## Redistribution boundary
The root [LICENSE](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/LICENSE) permits source and binary redistribution with or without changes if its copyright notice, conditions, and disclaimer are retained or reproduced as specified. This is not a blanket license for all included third-party work. The [TodoMVC subtree license](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/license.md) states MIT unless otherwise specified and requires inclusion of its notice in copies or substantial portions. Built bundles also carry license files, including [React bundle notices](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/architecture-examples/react/dist/app.bundle.js.LICENSE.txt), [Angular third-party notices](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/architecture-examples/angular/dist/3rdpartylicenses.txt), and [React Stockcharts notices](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/react-stockcharts/build/static/js/2.8e539c84.chunk.js.LICENSE.txt).
Pack the complete upstream notice files alongside their assets, preserve inline notices, and record a notice inventory with the pack manifest. A complete third-party license audit remains unresolved: the archive includes many generated bundles and assets, and finding a root license plus named notice files does not establish licensing for every individual asset. Review the final redistributed file set and notices before shipping. This is a licensing assessment from primary source text, not legal advice.
## Browser mode and result identity
Upstream [test instructions](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/instructions.html) call for a latest stable browser, clean profile, focused page, closed competing tabs, and no interaction during the run. The [page](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/index.html) warns when its visible viewport is below 850 × 650. The [parameters](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/params.mjs) separately default the suite iframe to 800 × 600 and expose iteration count, suite selection, and timing method. Record these exact conditions in Odin's run record.
Use a headed, focused browser session for the comparable browser workload. Upstream provides no headless equivalence claim in the cited instructions or [Selenium runner](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/tests/run.mjs). Headless may be useful for a smoke test, but treat any headless measurement as a distinct software mode until equivalence is demonstrated. Do not silently mix browser versions, profiles, window/iframe sizes, suite selections, or headed/headless results in one calibrated measurement.
## Manifest and offline acceptance proposal
Create a deterministic, content-addressed pack manifest. Record: upstream repository URL and full commit; Speedometer 3.1 display version; every shipped relative path with byte length and SHA-256; a sorted inventory of license/notice paths; default suite names; and packaging schema version. Hash canonical serialized manifest bytes for the pack identifier. Verify each file hash before serving; reject missing, extra, or changed files. Keep the complete source snapshot or an auditable mapping from snapshot to shipped subset. This is an Odin design recommendation based on the pinned [runner paths](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/benchmark-runner.mjs), [suite list](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/tests.mjs), and [license](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/LICENSE); upstream does not prescribe this manifest.
Before a benchmark run, validate offline behavior without collecting a score: serve the frozen tree on loopback, open the landing page and each default suite page in a disposable browser profile, disable outside network at the browser or sandbox boundary, log attempted requests, and check that all required assets load with no external request or console error. Include dynamic imports, CSS fonts/images, redirects, worker requests, and the Perf Dashboard JSON paths. Do not click Start Test during this gate. If the gate fails, report the missing/remote URL and mark the browser workload unavailable; do not substitute an online fetch. This acceptance procedure is proposed, not claimed as completed.
@@ -1,39 +0,0 @@
# Speedometer 3.1 offline pack: packaging decision
## Answer
**Do not redistribute an unchanged Speedometer 3.1 pack from commit `1386415be8fef2f6b6bbdbe1828872471c5d802a` yet.** The pinned archive is a conservative, precisely inventoried source candidate, but its full redistribution rights are not established. The companion [per-file source inventory](speedometer-license-manifest.tsv) contains all 1,118 archive files, each with byte count and SHA-256 (61,022,359 bytes total; inventory SHA-256 `78edcd45649128fbd4c8659c1d054a2b9bb76a184b58ce59b37895c19f2bdfb6`). It identifies review groups, **not** individual license clearance or a minimal runtime set. The [prior license audit](speedometer-license-audit.md) documents the evidence and gaps. Source: [pinned WebKit/Speedometer tree](https://github.com/WebKit/Speedometer/tree/1386415be8fef2f6b6bbdbe1828872471c5d802a).
The acquisition decision is concrete: obtain a verifiable grant and required notices for every unresolved work in the selected pack, or replace those works and rebuild/validate the affected assets. If either route cannot clear every **default** suite, do not call a reduced suite set the unchanged official Speedometer 3.1 workload. Select a different browser workload or explicitly specify a derivative suite and scoring identity. The [pinned suite list](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/tests.mjs) enables NewsSite Next/Nuxt, both Charts suites, both Editor suites, and complex TodoMVC variants by default, so simply deleting those assets changes the measured workload. No permission or replacement is established by this report.
## Asset and notice boundary
The [runner](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/benchmark-runner.mjs) loads `resources/${suite.url}`; the [suite list](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/tests.mjs) declares 32 suite entries, 20 enabled by default. All declared entry files exist in the pinned archive. Previous static inspection found 194 landing-page and default-entry HTML asset references, all local and present, but did not resolve dynamic imports, CSS URLs, route requests, or interactions. Source: [prior offline-pack research](speedometer-offline-pack.md), [pinned archive](https://github.com/WebKit/Speedometer/tree/1386415be8fef2f6b6bbdbe1828872471c5d802a). No exact smaller **runtime** file set has therefore been proved. Use the full archive as the conservative candidate until a dependency trace and offline gate justify exclusions.
| Asset group in candidate archive | Evidence and obligation or gap |
| --- | --- |
| Speedometer-authored files | [Root BSD-style license](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/LICENSE): retain its copyright, conditions, and disclaimer in source copies; reproduce them in documentation or other binary-distribution materials. Its terms do not establish rights to embedded third-party works. |
| TodoMVC implementations and generated framework bundles | [TodoMVC license](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/license.md) says MIT unless otherwise specified. Keep that text, matching generated-bundle notices, and inline notices; match bundled component versions to source/lockfiles. [Angular notice](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/architecture-examples/angular/dist/3rdpartylicenses.txt) includes MIT, Apache-2.0, and CC-BY-4.0 entries. |
| NewsSite Next/Nuxt, template, CSS, imagery | Both [Next](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/newssite/news-next/README.md) and [Nuxt](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/newssite/news-nuxt/README.md) credit the [source template](https://github.com/flashdesignory/news-site-template), whose repository exposes no license file or package license. No grant for adapted template content is established. [Pinned CSS package metadata](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/newssite/news-site-css/package.json) says ISC, but the matching license text and rights for bundled images still need confirmation. |
| Charts code and data | [Dataset README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/charts/datasets/README) identifies copied CSVs without source rights. The archive also contains generated chart bundles, including data embedded in `resources/charts/dist/assets/flights-airports-9a9e6422.js`; the [manifest](speedometer-license-manifest.tsv) records their hashes. Trace dataset grants or replace with cleared equivalent data, rebuild, then recheck the result. |
| Complex TodoMVC Adobe shell | [Big DOM README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/todomvc/big-dom-generator/README.md) names Adobe `@spectrum-css`; the archive has `Smock_*.svg` icons. Exact package versions, rights, and built-copy notices remain unresolved. |
| Editor fixtures and bundles | [`longtext.html`](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/editors/assets/longtext.html) identifies Project Gutenberg eBook 2650 and contains its license. [Project Gutenberg's terms](https://www.gutenberg.org/policy/license) attach redistribution and trademark conditions while its marks remain; geographic rights need checking. Replace with independently cleared text of comparable workload shape if those conditions are unsuitable. Editor bundles also need component/notice mapping. |
| React Stockcharts and Perf Dashboard | [Stockcharts README](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/react-stockcharts/README.md) cites MIT; [generated bundle notice](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/react-stockcharts/build/static/js/2.8e539c84.chunk.js.LICENSE.txt) lists components. Keep notices and verify component versions. Audit generated dashboard JavaScript and imagery against their source rights. |
The candidate archive has nine separate notice files: root `LICENSE`; `resources/todomvc/license.md`; one React Stockcharts `*.LICENSE.txt`; two Angular `3rdpartylicenses.txt`; and four React-family `app.bundle.js.LICENSE.txt`. Their exact paths and hashes are in the [source inventory](speedometer-license-manifest.tsv). Preserve applicable files and inline headers. A top-level notice index must map each shipped component or asset to origin, copyright holder, license/permission evidence, required notice, and fulfillment location. A notice file alone does not cure an absent grant. Source: [pinned tree](https://github.com/WebKit/Speedometer/tree/1386415be8fef2f6b6bbdbe1828872471c5d802a), [license audit](speedometer-license-audit.md).
## Offline fetch boundary
The built pack can be served over loopback HTTP without installing its development dependencies; the [about page](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/about.html) says suites are static applications without server infrastructure. The [main page](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/index.html) loads local runner assets. The Perf Dashboard [static page](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/perf.webkit.org/public/v3/index.html) overrides `RemoteAPI.sendHttpRequest`, prefetches 13 local JSON fixtures, and reports unexpected paths. Those 13 paths exist in the archive; an unmocked [remote API implementation](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/perf.webkit.org/public/v3/remote.js) also exists, so packaging must retain the override and verify it executes. Static source inspection has **not** established closure for all dynamic requests, imports, CSS fonts/images, redirects, workers, or navigation. Source: [prior offline-pack research](speedometer-offline-pack.md).
Prepared native automation is outside the asset pack. The pinned [Selenium harness](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/tests/run.mjs) starts a server and connects to an installed browser/driver; [Testing.md](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/Testing.md) states those installation requirements for upstream tests. Odin's browser binary, driver, profile, window state, headed/headless mode, and automation adapter belong to the **run environment record**, not the frozen workload pack. Upstream [instructions](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/instructions.html) require a focused browser page. The [parameters](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/params.mjs) default the suite iframe to 800 × 600; the [landing page](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/index.html) warns below an 850 × 650 visible viewport. Record both sizes; keep headed and headless measurements separate unless equivalence is demonstrated.
## Final manifest and acceptance gate
After rights are cleared and the exact shipped file set is selected, create a canonical, content-addressed manifest **for that set**. Include schema version; upstream URL/full commit; Speedometer display version; immutable suite names, entry URLs, enabled state, and runner parameters; every normalized relative path with byte length and SHA-256; provenance and license evidence identifiers; notice paths and fulfillment mapping; and all replacements/build inputs. Sort paths by UTF-8 bytes, use one specified JSON serialization, exclude the manifest's own digest from hashed content, and publish the SHA-256 of those bytes as pack identity. Reject duplicate or traversal paths, symlinks, missing/extra files, and hash mismatches before serving. Any altered asset, suite list, or notice changes pack identity. This is an Odin packaging proposal; upstream provides no such manifest. Source for source identity and suite paths: [pinned tree](https://github.com/WebKit/Speedometer/tree/1386415be8fef2f6b6bbdbe1828872471c5d802a), [suite list](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/tests.mjs).
Run a separate **non-benchmark** acceptance gate before any scored use: verify the manifest; serve read-only files on loopback; start a disposable browser profile with outbound network blocked; open the landing page and each enabled suite entry without starting `Start Test`; capture all page/frame/worker requests and response status, redirects, console errors, and service-worker activity; inspect dynamic imports, CSS images/fonts, and the Perf Dashboard fixture requests. If a suite needs interaction to reveal a resource, use an unscored smoke action outside the runner and record it. Fail on any outside request, missing local resource, unexpected remote API path, or console error that affects loading. This tests reachability and fetch closure, **not** benchmark behavior or timing. A full workload execution later remains a separate validation before results are trusted. Source for runner behavior: [runner](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/benchmark-runner.mjs), [dashboard mock](https://github.com/WebKit/Speedometer/blob/1386415be8fef2f6b6bbdbe1828872471c5d802a/resources/perf.webkit.org/public/v3/index.html).
## State of evidence
No browser was installed, no benchmark was run, and no host setting was changed for this research. No pack has passed the legal or offline gate. The exact runtime subset, component-to-file rights matrix, successful fetch closure, and permission or replacement choices remain to be established before redistribution.