perf: enforce journey budgets and reference traces

This commit is contained in:
2026-09-02 07:48:02 +03:00
parent 8089d061ec
commit f4cf9fc77e
25 changed files with 1432 additions and 23 deletions

View File

@@ -0,0 +1,51 @@
# Portfolio performance lab runbook
This runbook separates executable developer evidence from the two physical release gates. A passing laptop/headless run never substitutes for the named hardware.
## Automated build and transfer gate
Run `npm run build:preview`. The build ends with `npm run check:budgets`, which classifies every shipped file exactly once, measures Brotli transfer bytes (or raw bytes for already-compressed formats), verifies that the live Three.js scene remains behind a dynamic import, and applies the exact section 14 thresholds. Unknown files fail closed.
The current empty-media preview measures approximately 7.8 KB for HTML plus critical CSS, 61.8 KB for initial application JavaScript, and 114.2 KB for the deferred first live scene. These values are build observations, not permanent allowances.
## Current-Chromium developer trace
Run:
```bash
npx playwright test tests/performance/drive-trace.spec.ts \
--project=performance-desktop --project=performance-mobile --workers=1
```
The two 20-second traces use current Chromium, trusted CDP mouse/key/wheel input, the specified desktop/mobile network profiles, exact action offsets, fixed High/Low rendering tiers, the deterministic Toolkit cancellation barrier, and one fresh browser context per run. Each test writes `drive-trace.v1.json` under its ignored Playwright result directory.
Developer reports deliberately contain:
- `evidenceKind: current-chromium-developer-machine-non-reference`
- `gatingReferenceResult: false`
- individual budget booleans and measured values
- `pendingPhysicalReferenceRuns: 5`
They prove that the harness and interactions execute. They do not make a release claim, even if every number happens to pass. Instrumentation exists only in an authenticated test build, stays inside the page/test process, records no GPU or audio-device identity, and sends no telemetry.
## Five-run physical reference gate
Use only the Pixel 6 8 GB / Chromium Mobile Low and M1 MacBook Air 8 GB / Chromium Desktop High reference classes, or hardware with the same CPU/GPU and RAM. Fix both displays at 60 Hz; require battery above 50%, power saving off, a cool start, no foreground competition, page zoom 100%, full motion, Save-Data off, and the exact network shaping from section 14. Record OS/browser/display versions in the private lab evidence, not in shipped artifacts.
For each profile, run five fresh temporary profiles from `about:blank`. Clear service workers, Cache Storage, HTTP cache, cookies, local/session storage, and IndexedDB before every run. Set `PORTFOLIO_REFERENCE_LAB=1` so cadence and all per-run budgets become assertions. A warm-up median outside 16.667 ±1 ms invalidates the run.
Aggregate exactly five reviewed JSON paths:
```bash
npm run aggregate:performance -- run-1.json run-2.json run-3.json run-4.json run-5.json
```
Aggregation fails unless all five are marked reference evidence, every per-run gate passes, worst/median LCP are at most 3.0/2.5 seconds, and worst/median TBT are at most 300/200 ms. Do not commit result files until their metadata has passed privacy review.
## Physical ten-minute protocol
`tests/performance/physical-drive.protocol.ts` is the versioned `physical-drive.v1` action generator. A platform runner must consume it without changing timing or order. It uses only `driveTouch` touch and `driveKeys` key sources, round-half-up safe-viewport coordinates, 15×40 ms swipe moves plus a 400 ms pause, and the direction rule sampled at every cycle start.
The generator includes rotations, 30-second background periods, audio interruption, context loss/restore, the 389/390-second cycle exclusions, the 390-second Infrastructure barrier and 391-second production-epoch cancellation, live versus terminal-Static pause/resume branches, and Contact settlement at ten minutes. Mouse/pen fallback, scripted click/focus, altered offsets, or emulation in place of the physical devices invalidates evidence.
Physical iPhone/Pixel execution remains pending until those devices and platform automation drivers are available. Never mark those rows passed from desktop emulation.

View File

@@ -17,8 +17,10 @@
"verify:artifacts": "node scripts/verify-artifacts.mjs",
"prepare:assets": "node scripts/prepare-assets.mjs",
"verify:assets": "node scripts/prepare-assets.mjs --verify",
"check:budgets": "node scripts/check-bundle-budgets.mjs dist",
"aggregate:performance": "node scripts/aggregate-drive-traces.mjs",
"prepare:preview-assets": "node scripts/materialize-empty-asset-set.mjs",
"build:preview": "npm run prepare:preview-assets && npm run typecheck && npm run test:unit && npm run build:client && npm run build:ssr && npm run prerender -- --config config/site.preview.json && npm run verify:artifacts -- --config config/site.preview.json"
"build:preview": "npm run prepare:preview-assets && npm run typecheck && npm run test:unit && npm run build:client && npm run build:ssr && npm run prerender -- --config config/site.preview.json && npm run verify:artifacts -- --config config/site.preview.json && npm run check:budgets"
},
"keywords": [],
"author": "",

View File

@@ -1,14 +1,16 @@
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/browser',
testDir: './tests',
globalSetup: './tests/helpers/test-build-auth.ts',
workers: 4,
use: {
serviceWorkers: 'block',
},
projects: [
{ name: 'desktop-chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'mobile-chromium', use: { ...devices['Pixel 5'] } },
{ name: 'desktop-chromium', testMatch: /browser\/.*\.spec\.ts/u, use: { ...devices['Desktop Chrome'] } },
{ name: 'mobile-chromium', testMatch: /browser\/.*\.spec\.ts/u, use: { ...devices['Pixel 5'] } },
{ name: 'performance-desktop', testMatch: /performance\/drive-trace\.spec\.ts/u, use: { ...devices['Desktop Chrome'] } },
{ name: 'performance-mobile', testMatch: /performance\/drive-trace\.spec\.ts/u, use: { ...devices['Desktop Chrome'], viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true } },
],
});

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env node
import { readFile } from 'node:fs/promises';
import { aggregateFiveDriveTraces } from './lib/performance-aggregation.mjs';
const paths = process.argv.slice(2);
if (paths.length !== 5) throw new Error('Pass exactly five explicit drive-trace.v1 JSON paths.');
const runs = [];
for (const path of paths) runs.push(JSON.parse(await readFile(path, 'utf8')));
const aggregate = aggregateFiveDriveTraces(runs);
process.stdout.write(`${JSON.stringify(aggregate, null, 2)}\n`);
if (!aggregate.passes) process.exitCode = 1;

View File

@@ -0,0 +1,80 @@
#!/usr/bin/env node
import { brotliCompressSync, constants as zlibConstants } from 'node:zlib';
import { readFile, readdir, stat } from 'node:fs/promises';
import { relative, resolve, sep } from 'node:path';
import { classifyArtifact, evaluateBudgetTotals, PERFORMANCE_BUDGETS } from './lib/bundle-budgets.mjs';
const root = resolve(process.argv[2] ?? 'dist');
if (!(await stat(root)).isDirectory()) throw new Error('Bundle budget target must be a directory.');
const listFiles = async (directory) => {
const files = [];
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = resolve(directory, entry.name);
if (entry.isSymbolicLink()) throw new Error('Bundle budget input cannot contain symbolic links.');
if (entry.isDirectory()) files.push(...await listFiles(path));
else if (entry.isFile()) files.push(path);
else throw new Error('Bundle budget input contains an unsupported entry.');
}
return files;
};
const compressedSize = (path, bytes) => (/\.(?:avif|br|gif|jpe?g|mp4|png|webm|webp|woff2?)$/iu.test(path)
? bytes.byteLength
: brotliCompressSync(bytes, {
params: { [zlibConstants.BROTLI_PARAM_QUALITY]: 11 },
}).byteLength);
const records = [];
for (const absolute of await listFiles(root)) {
const path = relative(root, absolute).split(sep).join('/');
const bytes = await readFile(absolute);
const source = /\.(?:css|html|js|json|txt|xml)$/u.test(path) ? bytes.toString('utf8') : '';
const bucket = classifyArtifact(path, source);
records.push({ path, bucket, bytes: bytes.byteLength, transferBytes: compressedSize(path, bytes) });
}
const sum = (bucket) => records.filter((record) => record.bucket === bucket).reduce((total, record) => total + record.transferBytes, 0);
const totals = {
initialHtmlCriticalCss: sum('initial-html') + sum('critical-css'),
initialApplicationJs: sum('initial-application-js'),
heroPoster: sum('hero-poster'),
initialFonts: sum('initial-fonts'),
firstLiveScene: sum('first-live-scene'),
deferredProjectVisuals: sum('project-still') + sum('chart'),
deferredNonVideo: sum('first-live-scene') + sum('project-still') + sum('chart'),
};
for (const record of records.filter((item) => item.bucket === 'project-video')) {
if (record.transferBytes > PERFORMANCE_BUDGETS.projectVideo) {
throw new Error(`Project video exceeds its transfer budget: ${record.path}`);
}
}
for (const record of records.filter((item) => item.bucket === 'project-still')) {
if (record.transferBytes > PERFORMANCE_BUDGETS.projectStill) {
throw new Error(`Project still exceeds its transfer budget: ${record.path}`);
}
}
for (const record of records.filter((item) => item.bucket === 'chart')) {
if (record.transferBytes > PERFORMANCE_BUDGETS.chart) {
throw new Error(`Chart exceeds its transfer budget: ${record.path}`);
}
}
const entry = records.find((record) => record.bucket === 'initial-application-js');
const scene = records.find((record) => record.bucket === 'first-live-scene');
if (entry === undefined || scene === undefined) throw new Error('Both initial application and deferred live-scene chunks are required.');
const entrySource = await readFile(resolve(root, entry.path), 'utf8');
if (!/import\([^)]*environment-/u.test(entrySource)) throw new Error('The live scene is not separated behind a dynamic import.');
if (/WebGLRenderer|WebGLProgram|ShaderMaterial/u.test(entrySource)) throw new Error('Three.js/live-scene code leaked into the reduced-motion initial chunk.');
const result = evaluateBudgetTotals(totals);
const report = {
schema: 'portfolio-bundle-budget/v1',
compression: 'brotli-quality-11-or-raw-already-compressed',
totals,
records: records.sort((left, right) => left.path.localeCompare(right.path)),
violations: result.violations,
};
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
if (result.violations.length > 0) process.exitCode = 1;

View File

@@ -0,0 +1,5 @@
export const PERFORMANCE_BUDGETS: Readonly<Record<string, number>>;
export function classifyArtifact(path: string, source?: string): string;
export function evaluateBudgetTotals(totals: Readonly<Record<string, number>>): {
violations: Array<{ budget: string; actual: number; threshold: number }>;
};

View File

@@ -0,0 +1,51 @@
export const PERFORMANCE_BUDGETS = Object.freeze({
initialHtmlCriticalCss: 100 * 1024,
initialApplicationJs: 120 * 1024,
heroPoster: 220 * 1024,
initialFonts: 120 * 1024,
firstLiveScene: 650 * 1024,
projectVideo: 2.5 * 1024 * 1024,
projectVideoDurationSeconds: 10,
projectVideoWidth: 1280,
projectVideoHeight: 720,
projectStill: 400 * 1024,
chart: 100 * 1024,
deferredProjectVisuals: 2.5 * 1024 * 1024,
highTierExtras: 750 * 1024,
deferredNonVideo: 3.5 * 1024 * 1024,
imageSceneConcurrency: 2,
videoConcurrency: 1,
mobileTextureDimension: 2048,
lowTierGpuEstimate: 96 * 1024 * 1024,
highTierGpuEstimate: 192 * 1024 * 1024,
});
export const classifyArtifact = (path, source = '') => {
if (path === 'index.html') return 'initial-html';
if (/^assets\/index-[^/]+\.css$/u.test(path)) return 'critical-css';
if (/^assets\/index-[^/]+\.js$/u.test(path) && /import\(/u.test(source)) return 'initial-application-js';
if (/^assets\/environment-[^/]+\.js$/u.test(path)) return 'first-live-scene';
if (/\.(?:woff2?|ttf|otf)$/u.test(path)) return 'initial-fonts';
if (/^assets\/hero-[^/]+\.(?:avif|webp|png|jpe?g)$/u.test(path)) return 'hero-poster';
if (/^assets\/project-[^/]+\.(?:webm|mp4)$/u.test(path)) return 'project-video';
if (/^assets\/project-[^/]*chart[^/]*\.(?:avif|webp|png|jpe?g)$/u.test(path)) return 'chart';
if (/^assets\/project-[^/]+\.(?:avif|webp|png|jpe?g|svg)$/u.test(path)) return 'project-still';
if (/^(?:robots\.txt|sitemap\.xml|_headers\.example|_redirects\.example)$/u.test(path)) return 'release-metadata';
throw new Error(`Artifact is unclassified: ${path}`);
};
const thresholdByTotal = Object.freeze({
initialHtmlCriticalCss: PERFORMANCE_BUDGETS.initialHtmlCriticalCss,
initialApplicationJs: PERFORMANCE_BUDGETS.initialApplicationJs,
heroPoster: PERFORMANCE_BUDGETS.heroPoster,
initialFonts: PERFORMANCE_BUDGETS.initialFonts,
firstLiveScene: PERFORMANCE_BUDGETS.firstLiveScene,
deferredProjectVisuals: PERFORMANCE_BUDGETS.deferredProjectVisuals,
deferredNonVideo: PERFORMANCE_BUDGETS.deferredNonVideo,
});
export const evaluateBudgetTotals = (totals) => ({
violations: Object.entries(thresholdByTotal)
.filter(([budget, threshold]) => (totals[budget] ?? 0) > threshold)
.map(([budget, threshold]) => ({ budget, actual: totals[budget], threshold })),
});

View File

@@ -0,0 +1,7 @@
export function aggregateFiveDriveTraces(runs: readonly unknown[]): {
schema: 'drive-trace-aggregation/v1';
profile: string;
runCount: 5;
summary: Record<string, number | boolean>;
passes: boolean;
};

View File

@@ -0,0 +1,37 @@
const median = (values) => {
if (values.length === 0) throw new Error('At least one value is required.');
const sorted = [...values].sort((left, right) => left - right);
return sorted[Math.floor(sorted.length / 2)];
};
export const aggregateFiveDriveTraces = (runs) => {
if (!Array.isArray(runs) || runs.length !== 5) throw new Error('Exactly five drive-trace.v1 runs are required.');
const profile = runs[0]?.profile;
if (typeof profile !== 'string' || runs.some((run) => run?.schema !== 'drive-trace.v1' || run.profile !== profile)) {
throw new Error('All runs must use one drive-trace.v1 profile.');
}
const lcp = runs.map((run) => run.metrics.lcp);
const tbt = runs.map((run) => run.metrics.tbt);
const everyPerRunBudgetPass = runs.every((run) => Object.values(run.budgetResults).every(Boolean));
const referenceEvidence = runs.every((run) => run.gatingReferenceResult === true && run.validReferenceCadence === true);
const summary = {
lcpWorst: Math.max(...lcp),
lcpMedian: median(lcp),
tbtWorst: Math.max(...tbt),
tbtMedian: median(tbt),
everyPerRunBudgetPass,
referenceEvidence,
};
return {
schema: 'drive-trace-aggregation/v1',
profile,
runCount: 5,
summary,
passes: referenceEvidence
&& everyPerRunBudgetPass
&& summary.lcpWorst <= 3_000
&& summary.lcpMedian <= 2_500
&& summary.tbtWorst <= 300
&& summary.tbtMedian <= 200,
};
};

View File

@@ -16,6 +16,8 @@ const canonicalSectionIds = [
const prohibitedSurfaces = [
/__PORTFOLIO_INPUTS__/iu,
/__PORTFOLIO_TEST_HOOKS__/iu,
/__PORTFOLIO_PERFORMANCE__/iu,
/__portfolio_performance/iu,
/PORTFOLIO_TEST_TOKEN/iu,
/__PORTFOLIO_(?:TEST|INSTRUMENTATION)__/iu,
/__portfolio(?:Journey)?Instrumentation/iu,

View File

@@ -20,6 +20,9 @@ import type { PortfolioContent, SectionId } from '@/content/types';
import type { SceneHostState } from '@/composables/useSceneController';
import type { MotionPolicy, QualityTier } from '@/journey/motion-policy';
declare const __PORTFOLIO_TEST_BUILD__: boolean;
const TEST_BUILD = typeof __PORTFOLIO_TEST_BUILD__ !== 'undefined' && __PORTFOLIO_TEST_BUILD__;
const props = defineProps<{ content: Readonly<PortfolioContent> }>();
const canonicalSectionIds = [
@@ -57,7 +60,13 @@ const adaptive = useAdaptiveQuality({
sceneState,
pageLifecycle,
});
const actualQuality = computed(() => adaptive.snapshot.value.actual);
const requestedPerformanceTier = TEST_BUILD
? new URL(window.location.href).searchParams.get('__portfolio_tier')
: null;
const pinnedPerformanceTier = requestedPerformanceTier === 'high' || requestedPerformanceTier === 'low'
? requestedPerformanceTier
: null;
const actualQuality = computed(() => pinnedPerformanceTier ?? adaptive.snapshot.value.actual);
const activeSectionLabel = computed(() => sectionLabel(journey.value.activeSectionId));
const serverDefaultPolicy: MotionPolicy = {
allowSceneRequest: false,
@@ -70,6 +79,16 @@ const displayedQuality = computed<QualityTier>(() => controlsReady.value ? actua
const displayedSceneState = computed(() => controlsReady.value ? sceneState.value : 'running');
let removePageListeners = (): void => {};
const sampleSceneFrame = (start: number, end: number): void => {
if (pinnedPerformanceTier === null) adaptive.sampleFrame(start, end);
if (TEST_BUILD) window.__PORTFOLIO_PERFORMANCE__?.recordSceneFrame(start, end);
};
const updateSceneState = (state: SceneHostState): void => {
sceneState.value = state;
if (TEST_BUILD) window.__PORTFOLIO_PERFORMANCE__?.recordSceneState(state);
};
watch(
() => journey.value.activeSectionId,
(id) => navigation.notePassiveSection(id),
@@ -100,6 +119,28 @@ const enableMotion = (): void => {
const resumeMotion = (): void => {
motion.setPaused(false);
adaptive.setMaximum(motion.policy.value.maximumTier, 'manual');
if (TEST_BUILD) window.__PORTFOLIO_PERFORMANCE__?.recordActionMutation('resume');
};
const pauseMotion = (): void => {
motion.setPaused(true);
if (TEST_BUILD) window.__PORTFOLIO_PERFORMANCE__?.recordActionMutation('pause');
};
const startDrive = (): void => {
audio.startFromGesture();
if (TEST_BUILD) window.__PORTFOLIO_PERFORMANCE__?.recordActionMutation('start');
};
const toggleAudioMuted = (): void => {
const action = audio.muted.value ? 'unmute' : 'mute';
audio.toggleMuted();
if (TEST_BUILD) window.__PORTFOLIO_PERFORMANCE__?.recordActionMutation(action);
};
const updateAudioVolume = (value: number): void => {
audio.setVolume(value);
if (TEST_BUILD && value === 0.35) window.__PORTFOLIO_PERFORMANCE__?.recordActionMutation('volume');
};
onMounted(() => {
@@ -126,8 +167,8 @@ onUnmounted(() => removePageListeners());
:policy="motion.policy.value"
:quality="actualQuality"
:scene-description="content.sceneDescription"
:sample-frame="adaptive.sampleFrame"
@state-change="sceneState = $event"
:sample-frame="sampleSceneFrame"
@state-change="updateSceneState"
/>
<JourneyHud
:journey="journey"
@@ -203,13 +244,13 @@ onUnmounted(() => removePageListeners());
:audio-state="audio.state.value"
:audio-muted="audio.muted.value"
:audio-volume="controlsReady ? motion.volume.value : 0.4"
@pause="motion.setPaused(true)"
@pause="pauseMotion"
@resume="resumeMotion"
@enable="enableMotion"
@quality-change="setQuality"
@audio-start="audio.startFromGesture"
@audio-start="startDrive"
@audio-resume="audio.resumeFromGesture"
@audio-mute-toggle="audio.toggleMuted"
@audio-volume-change="audio.setVolume"
@audio-mute-toggle="toggleAudioMuted"
@audio-volume-change="updateAudioVolume"
/>
</template>

View File

@@ -86,9 +86,6 @@ const changeVolume = (event: Event): void => {
type="button"
@click="emit('audioResume')"
>Resume sound</button>
<button type="button" @click="emit('audioMuteToggle')">
{{ audioMuted ? 'Unmute sound' : 'Mute sound' }}
</button>
<label>
<span>Sound volume</span>
<input
@@ -101,6 +98,9 @@ const changeVolume = (event: Event): void => {
@input="changeVolume"
>
</label>
<button type="button" @click="emit('audioMuteToggle')">
{{ audioMuted ? 'Unmute sound' : 'Mute sound' }}
</button>
</div>
<label>
<span>Quality</span>

View File

@@ -7,6 +7,17 @@ const props = defineProps<{
journey: JourneySnapshot;
onSectionLinkClick: (event: MouseEvent) => void;
}>();
const compactLabels: Readonly<Record<SectionId, string>> = {
start: 'Start',
profile: 'Profile',
experience: 'Career',
infrastructure: 'Infra',
'selected-builds': 'Builds',
'additional-work': 'More work',
toolkit: 'Toolkit',
contact: 'Contact',
};
</script>
<template>
@@ -15,11 +26,13 @@ const props = defineProps<{
<li v-for="item in items" :key="item.id">
<a
:href="`#${item.id}`"
:aria-label="item.label"
:data-compact-label="compactLabels[item.id]"
:aria-current="
props.journey.activeSectionId === item.id ? 'page' : undefined
"
@click="props.onSectionLinkClick"
>{{ item.label }}</a
><span class="site-navigation__full-label" aria-hidden="true">{{ item.label }}</span></a
>
</li>
</ul>

View File

@@ -13,6 +13,14 @@ if (requestsTestFault) {
await installAuthorizedTestHooks();
}
const requestsPerformanceTrace = TEST_BUILD
&& new URL(location.href).searchParams.get('__portfolio_performance') === 'drive-trace.v1'
&& new URL(location.href).searchParams.has('__portfolio_test_token');
if (requestsPerformanceTrace) {
const { installAuthorizedPerformanceInstrumentation } = await import('./testing/performance-instrumentation');
await installAuthorizedPerformanceInstrumentation();
}
export const readProductionInputsSnapshot = (source: Document): Readonly<ProductionInputs> => {
const snapshot = source.getElementById('portfolio-inputs');
if (

View File

@@ -1,6 +1,9 @@
import type { SectionId } from "@/content/types";
import type { JourneySource } from "./types";
declare const __PORTFOLIO_TEST_BUILD__: boolean;
const TEST_BUILD = typeof __PORTFOLIO_TEST_BUILD__ !== 'undefined' && __PORTFOLIO_TEST_BUILD__;
export interface NavigationState {
epoch: number;
source: JourneySource;
@@ -162,6 +165,10 @@ export function createNavigationController(
source,
destinationId,
};
if (TEST_BUILD) window.__PORTFOLIO_PERFORMANCE__?.noteNavigationEpoch(currentState.epoch);
if (TEST_BUILD && source === 'explicit-navigation' && (destinationId === 'toolkit' || destinationId === 'contact')) {
window.__PORTFOLIO_PERFORMANCE__?.recordActionMutation(destinationId);
}
emit();
return currentState.epoch;
};
@@ -184,6 +191,7 @@ export function createNavigationController(
) {
return;
}
if (TEST_BUILD && window.__PORTFOLIO_PERFORMANCE__?.holdNavigationSettlement(epoch)) return;
cancelSettle();
frameId = runtime.requestFrame(() => {
frameId = undefined;
@@ -202,6 +210,7 @@ export function createNavigationController(
id: SectionId,
target: HTMLElement,
): void => {
if (TEST_BUILD) window.__PORTFOLIO_PERFORMANCE__?.registerNavigationSettlement(epoch);
settleTimerId = runtime.setTimer(() => {
settleTimerId = undefined;
if (

View File

@@ -6,7 +6,7 @@
display: grid;
gap: var(--space-2);
min-width: 0;
padding: var(--space-4) 4vw var(--space-3);
padding: calc(var(--space-4) + 44px) 4vw var(--space-3);
border-block-end: var(--panel-border);
background: #0b111b;
}
@@ -21,14 +21,20 @@
}
.site-navigation {
margin-block-start: var(--space-1);
position: fixed;
z-index: 3;
inset: 0 0 auto;
min-block-size: 44px;
border-block-end: var(--panel-border);
background: rgb(7 13 23 / 94%);
backdrop-filter: blur(10px);
}
.site-navigation ul {
display: flex;
flex-wrap: wrap;
display: grid;
grid-template-columns: repeat(8, minmax(0, 1fr));
gap: 0.25rem;
max-inline-size: 78rem;
max-inline-size: 90rem;
margin: 0;
padding: 0;
list-style: none;
@@ -38,7 +44,8 @@
display: inline-flex;
align-items: center;
min-block-size: 44px;
padding-inline: 0.7rem;
justify-content: center;
padding-inline: 0.4rem;
color: var(--text-muted);
font-family: var(--mono);
font-size: var(--step--1);
@@ -63,7 +70,7 @@
min-width: 0;
margin: 0 1rem var(--space-4);
padding: var(--space-3);
scroll-margin-block: var(--space-3);
scroll-margin-block-start: calc(44px + var(--space-3));
border: var(--panel-border);
border-radius: 2px;
background: var(--surface);
@@ -89,6 +96,26 @@
}
@media (max-width: 767px) {
.site-header {
padding-block-start: calc(var(--space-4) + 88px);
}
.site-navigation ul {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.site-navigation__full-label {
display: none;
}
.site-navigation a::after {
content: attr(data-compact-label);
}
.portfolio-section {
scroll-margin-block-start: calc(88px + var(--space-3));
}
.project-link,
.contact-link,
.contact-social-link {

View File

@@ -0,0 +1,394 @@
declare const __PORTFOLIO_TEST_BUILD__: boolean;
declare const __PORTFOLIO_TEST_TOKEN_DIGEST__: string;
const TEST_BUILD = typeof __PORTFOLIO_TEST_BUILD__ !== 'undefined' && __PORTFOLIO_TEST_BUILD__;
const TEST_TOKEN_DIGEST = typeof __PORTFOLIO_TEST_TOKEN_DIGEST__ === 'undefined'
? ''
: __PORTFOLIO_TEST_TOKEN_DIGEST__;
export interface LayoutShiftSample {
startTime: number;
value: number;
hadRecentInput: boolean;
}
export interface LongTaskSample {
startTime: number;
duration: number;
}
export interface EventTimingSample {
target: string;
startTime: number;
duration: number;
interactionId: number;
}
export interface ScriptedActionSample {
id: string;
target: string;
dispatchTime: number;
acknowledgementCount: number;
mutationTime: number | null;
doubleRafTime: number | null;
eventTimingSupported: boolean;
eventEntries: readonly EventTimingSample[];
}
export interface SceneInterval { start: number; end: number }
export interface SceneFrameSample { start: number; end: number }
export const nearestRankP95 = (samples: readonly number[]): number => {
if (samples.length === 0) throw new Error('At least one sample is required.');
const sorted = [...samples].sort((left, right) => left - right);
return sorted[Math.ceil(0.95 * sorted.length) - 1]!;
};
export const latestLcpBeforeCutoff = (entries: readonly number[], cutoff: number): number => {
const eligible = entries.filter((entry) => entry < cutoff);
if (eligible.length === 0) throw new Error('An LCP entry is required before the cutoff.');
return Math.max(...eligible);
};
export const calculateCls = (entries: readonly LayoutShiftSample[]): number => {
const eligible = entries
.filter((entry) => !entry.hadRecentInput)
.sort((left, right) => left.startTime - right.startTime);
let maximum = 0;
let windowStart = -Infinity;
let previous = -Infinity;
let sum = 0;
for (const entry of eligible) {
if (entry.startTime - previous > 1_000 || entry.startTime - windowStart > 5_000) {
windowStart = entry.startTime;
sum = 0;
}
sum += entry.value;
previous = entry.startTime;
maximum = Math.max(maximum, sum);
}
return maximum;
};
export const calculateLabTbt = (
tasks: readonly LongTaskSample[],
fcp: number | null,
runningAt: number,
): number => {
if (fcp === null) throw new Error('FCP is required for lab total blocking time.');
return tasks.reduce((total, task) => {
const start = Math.max(task.startTime + 50, fcp);
const end = Math.min(task.startTime + task.duration, runningAt);
return total + Math.max(0, end - start);
}, 0);
};
export const postRunningLongTasks = (
tasks: readonly LongTaskSample[],
runningAt: number,
traceEnd: number,
): LongTaskSample[] => tasks.filter((task) => (
task.startTime < traceEnd && task.startTime + task.duration > runningAt
));
const exactActionIds = new Set(['start', 'volume', 'mute', 'unmute', 'pause', 'resume', 'toolkit', 'contact']);
export const evaluateEventToNextPaint = (
actions: readonly ScriptedActionSample[],
): { values: number[]; p95: number } => {
if (actions.length !== 8 || new Set(actions.map((action) => action.id)).size !== 8 || actions.some((action) => !exactActionIds.has(action.id))) {
throw new Error('The exact eight scripted actions are required.');
}
const values = actions.map((action) => {
if (action.acknowledgementCount !== 1) throw new Error('Each action requires exactly one acknowledgement.');
if (action.mutationTime === null || action.mutationTime > action.dispatchTime + 500) throw new Error('The exact state mutation missed its deadline.');
if (action.doubleRafTime === null || action.doubleRafTime > action.dispatchTime + 500) throw new Error('Double RAF confirmation missed its deadline.');
if (!action.eventTimingSupported) throw new Error('Event Timing observation is unsupported.');
const matching = action.eventEntries.filter((entry) => (
entry.target === action.target
&& entry.startTime >= action.dispatchTime
&& entry.startTime <= action.dispatchTime + 500
));
if (matching.length === 0) return 16;
const groups = new Map<number, number>();
for (const entry of matching) {
if (entry.interactionId === 0) continue;
groups.set(entry.interactionId, Math.max(groups.get(entry.interactionId) ?? 0, entry.duration));
}
if (groups.size !== 1) throw new Error('Event Timing groups are ambiguous.');
return [...groups.values()][0]!;
});
return { values, p95: nearestRankP95(values) };
};
export const evaluateSceneContinuity = (
intervals: readonly SceneInterval[],
callbacks: readonly SceneFrameSample[],
expectedFramePeriod: number,
): { passes: boolean; expectedCallbacks: number; actualCallbacks: number } => {
if (!(expectedFramePeriod > 0)) throw new Error('Expected frame period must be positive.');
const eligible = intervals.filter((interval) => interval.end - interval.start >= expectedFramePeriod);
let expectedCallbacks = 0;
let actualCallbacks = 0;
let boundariesPass = eligible.length > 0;
for (const interval of eligible) {
expectedCallbacks += Math.floor((interval.end - interval.start) / expectedFramePeriod);
const inside = callbacks.filter((sample) => sample.start >= interval.start && sample.end <= interval.end);
actualCallbacks += inside.length;
if (inside.length === 0) {
boundariesPass = false;
continue;
}
if (inside[0]!.start - interval.start > 3 * expectedFramePeriod) boundariesPass = false;
for (let index = 1; index < inside.length; index += 1) {
if (inside[index]!.start - inside[index - 1]!.start > 3 * expectedFramePeriod) boundariesPass = false;
}
if (interval.end - inside.at(-1)!.end > 3 * expectedFramePeriod) boundariesPass = false;
}
const coverage = expectedCallbacks > 0 ? actualCallbacks / expectedCallbacks : 0;
return { passes: boundariesPass && coverage >= 0.9, expectedCallbacks, actualCallbacks };
};
export const calculateLostPresentationShare = (
rafTimestamps: readonly number[],
expectedFramePeriod: number,
minimumOpportunities: number,
): { opportunities: number; lost: number; share: number; hasSufficientCoverage: boolean } => {
let opportunities = 0;
let lost = 0;
for (let index = 1; index < rafTimestamps.length; index += 1) {
const interval = rafTimestamps[index]! - rafTimestamps[index - 1]!;
const nextOpportunities = Math.max(1, Math.round(interval / expectedFramePeriod));
opportunities += nextOpportunities;
lost += nextOpportunities - 1;
}
return {
opportunities,
lost,
share: opportunities === 0 ? 1 : lost / opportunities,
hasSufficientCoverage: opportunities >= minimumOpportunities,
};
};
export const unexpectedLateShifts = (
entries: readonly LayoutShiftSample[],
firstSceneFrame: number,
traceEnd: number,
): LayoutShiftSample[] => entries.filter((entry) => (
!entry.hadRecentInput && entry.startTime >= firstSceneFrame && entry.startTime < traceEnd
));
interface PerformanceSnapshot {
version: 'drive-trace.v1';
lcp: readonly number[];
layoutShifts: readonly LayoutShiftSample[];
longTasks: readonly LongTaskSample[];
pageRafs: readonly number[];
sceneFrames: readonly SceneFrameSample[];
sceneIntervals: readonly SceneInterval[];
firstSceneFrame: number | null;
traceStart: number | null;
traceEnd: number | null;
cutoff: number;
navigationBarrier: { armed: boolean; pendingEpoch: number | null; cancelledEpoch: number | null };
actions: readonly ScriptedActionSample[];
}
interface PerformanceHooks {
readonly version: 'drive-trace.v1';
startTrace(): void;
endTrace(): void;
recordSceneFrame(start: number, end: number): void;
recordSceneState(state: string): void;
armNavigationSettlementBarrier(): void;
registerNavigationSettlement(epoch: number): void;
holdNavigationSettlement(epoch: number): boolean;
noteNavigationEpoch(epoch: number): void;
beginAction(id: string, selector: string): void;
recordActionMutation(id: string): void;
snapshot(): PerformanceSnapshot;
}
declare global {
interface Window {
__PORTFOLIO_PERFORMANCE__?: PerformanceHooks;
}
}
const digest = async (value: string): Promise<string> => {
const output = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));
return [...new Uint8Array(output)].map((byte) => byte.toString(16).padStart(2, '0')).join('');
};
export const installAuthorizedPerformanceInstrumentation = async (): Promise<void> => {
if (!TEST_BUILD) return;
const url = new URL(location.href);
if (url.searchParams.get('__portfolio_performance') !== 'drive-trace.v1') return;
const token = url.searchParams.get('__portfolio_test_token');
if (token === null || await digest(token) !== TEST_TOKEN_DIGEST) return;
const lcp: number[] = [];
const layoutShifts: LayoutShiftSample[] = [];
const longTasks: LongTaskSample[] = [];
const pageRafs: number[] = [];
const sceneFrames: SceneFrameSample[] = [];
const sceneIntervals: SceneInterval[] = [];
let firstSceneFrame: number | null = null;
let traceStart: number | null = null;
let traceEnd: number | null = null;
let runningIntervalStart: number | null = null;
let cutoff = Infinity;
let sentinel = 0;
let barrierArmed = false;
let barrierPendingEpoch: number | null = null;
let barrierCancelledEpoch: number | null = null;
const actionRecords = new Map<string, ScriptedActionSample & { element: Element | null }>();
const observe = (type: string, callback: (entry: PerformanceEntry) => void): void => {
if (typeof PerformanceObserver === 'undefined') return;
try {
const observer = new PerformanceObserver((list) => list.getEntries().forEach(callback));
observer.observe({ type, buffered: true });
} catch { /* unsupported entry type */ }
};
observe('largest-contentful-paint', (entry) => lcp.push(entry.startTime));
observe('layout-shift', (entry) => {
const shift = entry as PerformanceEntry & { value?: number; hadRecentInput?: boolean };
layoutShifts.push({ startTime: shift.startTime, value: shift.value ?? 0, hadRecentInput: shift.hadRecentInput ?? false });
});
observe('longtask', (entry) => longTasks.push({ startTime: entry.startTime, duration: entry.duration }));
let eventTimingSupported = false;
if (typeof PerformanceObserver !== 'undefined') {
try {
const eventObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const timed = entry as PerformanceEntry & { interactionId?: number; target?: Node | null };
for (const record of actionRecords.values()) {
if (timed.target === record.element) {
(record.eventEntries as EventTimingSample[]).push({
target: record.id,
startTime: timed.startTime,
duration: timed.duration,
interactionId: timed.interactionId ?? 0,
});
}
}
}
});
const eventOptions = { type: 'event', buffered: true, durationThreshold: 16 } as PerformanceObserverInit & { durationThreshold: number };
eventObserver.observe(eventOptions);
eventTimingSupported = true;
} catch { /* Event Timing unsupported */ }
}
const cutOff = (event: Event): void => {
if (event.isTrusted) cutoff = Math.min(cutoff, performance.now());
};
addEventListener('pointerdown', cutOff, { capture: true, once: true });
addEventListener('keydown', cutOff, { capture: true, once: true });
document.addEventListener('visibilitychange', () => {
if (document.hidden) cutoff = Math.min(cutoff, performance.now());
});
const sentinelFrame = (now: number): void => {
if (traceStart !== null && traceEnd === null) pageRafs.push(now);
sentinel = requestAnimationFrame(sentinelFrame);
};
sentinel = requestAnimationFrame(sentinelFrame);
const hooks: PerformanceHooks = {
version: 'drive-trace.v1',
startTrace() {
traceStart = performance.now();
traceEnd = null;
pageRafs.length = 0;
sceneFrames.length = 0;
sceneIntervals.length = 0;
runningIntervalStart = document.querySelector('.journey-world')?.getAttribute('data-scene-state') === 'running' ? traceStart : null;
},
endTrace() {
traceEnd = performance.now();
if (runningIntervalStart !== null) {
sceneIntervals.push({ start: runningIntervalStart, end: traceEnd });
runningIntervalStart = null;
}
cancelAnimationFrame(sentinel);
},
recordSceneFrame(start, end) {
if (firstSceneFrame === null) firstSceneFrame = end;
if (traceStart !== null && traceEnd === null) sceneFrames.push({ start, end });
},
recordSceneState(state) {
const now = performance.now();
if (traceStart === null || traceEnd !== null) return;
if (state === 'running' && runningIntervalStart === null) runningIntervalStart = now;
if (state !== 'running' && runningIntervalStart !== null) {
sceneIntervals.push({ start: runningIntervalStart, end: now });
runningIntervalStart = null;
}
},
armNavigationSettlementBarrier() {
barrierArmed = true;
barrierPendingEpoch = null;
barrierCancelledEpoch = null;
},
registerNavigationSettlement(epoch) {
if (barrierArmed) barrierPendingEpoch = epoch;
},
holdNavigationSettlement(epoch) {
return barrierArmed && barrierPendingEpoch === epoch;
},
noteNavigationEpoch(epoch) {
if (barrierArmed && barrierPendingEpoch !== null && barrierPendingEpoch !== epoch) {
barrierCancelledEpoch = barrierPendingEpoch;
barrierPendingEpoch = null;
barrierArmed = false;
}
},
beginAction(id, selector) {
if (!exactActionIds.has(id) || actionRecords.has(id)) throw new Error('The performance action is invalid or duplicated.');
const element = document.querySelector(selector);
if (!(element instanceof Element)) throw new Error('The performance action target is unavailable.');
actionRecords.set(id, {
id,
target: id,
dispatchTime: performance.now(),
acknowledgementCount: 0,
mutationTime: null,
doubleRafTime: null,
eventTimingSupported,
eventEntries: [],
element,
});
},
recordActionMutation(id) {
const record = actionRecords.get(id);
if (record === undefined) return;
record.acknowledgementCount += 1;
record.mutationTime = performance.now();
requestAnimationFrame(() => requestAnimationFrame(() => { record.doubleRafTime = performance.now(); }));
},
snapshot: () => Object.freeze({
version: 'drive-trace.v1' as const,
lcp: Object.freeze([...lcp]),
layoutShifts: Object.freeze([...layoutShifts]),
longTasks: Object.freeze([...longTasks]),
pageRafs: Object.freeze([...pageRafs]),
sceneFrames: Object.freeze([...sceneFrames]),
sceneIntervals: Object.freeze([...sceneIntervals]),
firstSceneFrame,
traceStart,
traceEnd,
cutoff,
navigationBarrier: Object.freeze({ armed: barrierArmed, pendingEpoch: barrierPendingEpoch, cancelledEpoch: barrierCancelledEpoch }),
actions: Object.freeze([...actionRecords.values()].map(({ element: _element, ...record }) => Object.freeze({
...record,
eventEntries: Object.freeze([...record.eventEntries]),
}))),
}),
};
Object.defineProperty(window, '__PORTFOLIO_PERFORMANCE__', {
configurable: false,
enumerable: false,
writable: false,
value: Object.freeze(hooks),
});
};

View File

@@ -97,6 +97,16 @@ export const wrongTokenFaultUrl = (origin: string, fault: PortfolioFaultId): str
fault,
);
export const authorizedPerformanceUrl = (origin: string, tier: 'high' | 'low'): string => {
const token = process.env.PORTFOLIO_TEST_TOKEN;
if (token === undefined) throw new Error('The per-run portfolio test token is unavailable.');
const url = new URL(origin);
url.searchParams.set('__portfolio_performance', 'drive-trace.v1');
url.searchParams.set('__portfolio_test_token', token);
url.searchParams.set('__portfolio_tier', tier);
return url.href;
};
export interface BrowserErrorCollector {
readonly pageErrors: string[];
}

View File

@@ -0,0 +1,30 @@
import { describe, expect, it } from 'vitest';
import { aggregateFiveDriveTraces } from '../../scripts/lib/performance-aggregation.mjs';
const run = (lcp: number, tbt: number, gatingReferenceResult = true) => ({
schema: 'drive-trace.v1',
profile: 'desktop-high',
gatingReferenceResult,
validReferenceCadence: true,
metrics: { lcp, tbt },
budgetResults: { cls: true, scene: true },
});
describe('five-run drive trace aggregation', () => {
it('uses worst and median gates across exactly five reference runs', () => {
expect(aggregateFiveDriveTraces([
run(2_000, 100), run(2_100, 110), run(2_500, 200), run(2_900, 250), run(3_000, 300),
])).toMatchObject({
runCount: 5,
passes: true,
summary: { lcpWorst: 3_000, lcpMedian: 2_500, tbtWorst: 300, tbtMedian: 200 },
});
});
it('rejects non-reference developer evidence and wrong run counts', () => {
expect(aggregateFiveDriveTraces([
run(2_000, 100, false), run(2_000, 100), run(2_000, 100), run(2_000, 100), run(2_000, 100),
]).passes).toBe(false);
expect(() => aggregateFiveDriveTraces([run(2_000, 100)])).toThrow(/five/u);
});
});

View File

@@ -0,0 +1,41 @@
import { describe, expect, it } from 'vitest';
import { classifyArtifact, evaluateBudgetTotals, PERFORMANCE_BUDGETS } from '../../scripts/lib/bundle-budgets.mjs';
describe('release budgets', () => {
it('pins every exact section 14 threshold', () => {
expect(PERFORMANCE_BUDGETS).toMatchObject({
initialHtmlCriticalCss: 100 * 1024,
initialApplicationJs: 120 * 1024,
heroPoster: 220 * 1024,
initialFonts: 120 * 1024,
firstLiveScene: 650 * 1024,
projectVideo: 2.5 * 1024 * 1024,
projectVideoDurationSeconds: 10,
projectVideoWidth: 1280,
projectVideoHeight: 720,
projectStill: 400 * 1024,
chart: 100 * 1024,
deferredProjectVisuals: 2.5 * 1024 * 1024,
highTierExtras: 750 * 1024,
deferredNonVideo: 3.5 * 1024 * 1024,
imageSceneConcurrency: 2,
videoConcurrency: 1,
mobileTextureDimension: 2048,
lowTierGpuEstimate: 96 * 1024 * 1024,
highTierGpuEstimate: 192 * 1024 * 1024,
});
});
it('classifies every artifact once and keeps the scene chunk deferred', () => {
expect(classifyArtifact('index.html', '<script src="/assets/index-a.js"></script>')).toBe('initial-html');
expect(classifyArtifact('assets/index-a.css', '')).toBe('critical-css');
expect(classifyArtifact('assets/index-a.js', 'import("./environment-b.js")')).toBe('initial-application-js');
expect(classifyArtifact('assets/environment-b.js', 'three scene')).toBe('first-live-scene');
expect(() => classifyArtifact('assets/mystery.bin', '')).toThrow(/unclassified/u);
});
it('fails exact byte boundaries and accepts equality', () => {
expect(evaluateBudgetTotals({ initialHtmlCriticalCss: 100 * 1024 }).violations).toEqual([]);
expect(evaluateBudgetTotals({ initialHtmlCriticalCss: 100 * 1024 + 1 }).violations).toContainEqual(expect.objectContaining({ budget: 'initialHtmlCriticalCss' }));
});
});

View File

@@ -0,0 +1,209 @@
import { performance as nodePerformance } from 'node:perf_hooks';
import { writeFile } from 'node:fs/promises';
import type { CDPSession, Page } from '@playwright/test';
import { expect, test } from '../helpers/serve-dist';
import { authorizedPerformanceUrl } from '../helpers/test-build-auth';
import {
calculateCls,
calculateLabTbt,
calculateLostPresentationShare,
evaluateEventToNextPaint,
evaluateSceneContinuity,
latestLcpBeforeCutoff,
nearestRankP95,
postRunningLongTasks,
unexpectedLateShifts,
type ScriptedActionSample,
} from '../../src/testing/performance-instrumentation';
type NamedProfile = 'desktop-high' | 'mobile-low';
const profileForProject = (name: string): {
name: NamedProfile;
tier: 'high' | 'low';
viewport: { width: number; height: number };
latency: number;
download: number;
upload: number;
sceneCpuP95: number;
lostShare: number;
} => name === 'performance-mobile'
? { name: 'mobile-low', tier: 'low', viewport: { width: 390, height: 844 }, latency: 150, download: 200_000, upload: 93_750, sceneCpuP95: 24, lostShare: 0.1 }
: { name: 'desktop-high', tier: 'high', viewport: { width: 1_440, height: 900 }, latency: 40, download: 1_250_000, upload: 1_250_000, sceneCpuP95: 12, lostShare: 0.05 };
const sleep = (duration: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, duration));
const stableTargetCenter = async (page: Page, selector: string) => {
const locator = page.locator(selector).filter({ visible: true });
await expect(locator).toHaveCount(1);
await expect(locator).toBeEnabled();
const box = await locator.boundingBox();
if (box === null) throw new Error(`Trace target is not visible: ${selector}`);
return { x: box.x + box.width / 2, y: box.y + box.height / 2 };
};
const dispatchPointerAction = async (
page: Page,
cdp: CDPSession,
at: (offset: number) => Promise<void>,
offset: number,
id: string,
selector: string,
) => {
await at(offset - 50);
const center = await stableTargetCenter(page, selector);
const markerSelector = `[data-drive-target="${id}"]`;
await page.locator(selector).filter({ visible: true }).evaluate((element, actionId) => {
element.setAttribute('data-drive-target', actionId);
}, id);
await at(offset - 20);
await cdp.send('Input.dispatchMouseEvent', { type: 'mouseMoved', ...center });
await at(offset);
await page.evaluate(({ actionId, target }) => window.__PORTFOLIO_PERFORMANCE__?.beginAction(actionId, target), { actionId: id, target: markerSelector });
await cdp.send('Input.dispatchMouseEvent', { type: 'mousePressed', button: 'left', clickCount: 1, ...center });
await at(offset + 40);
await cdp.send('Input.dispatchMouseEvent', { type: 'mouseReleased', button: 'left', clickCount: 1, ...center });
};
const dispatchWheelBurst = async (page: Page, cdp: CDPSession, at: (offset: number) => Promise<void>, offset: number, deltaY: number) => {
const center = await page.evaluate(() => {
const viewport = window.visualViewport;
return {
x: (viewport?.offsetLeft ?? 0) + (viewport?.width ?? innerWidth) / 2,
y: (viewport?.offsetTop ?? 0) + (viewport?.height ?? innerHeight) / 2,
};
});
for (const deltaOffset of [0, 40, 80, 120, 160, 200]) {
await at(offset + deltaOffset);
await cdp.send('Input.dispatchMouseEvent', { type: 'mouseWheel', deltaX: 0, deltaY, ...center });
}
};
test('drive-trace.v1 runs on current Chromium as explicitly non-reference evidence', async ({ page, distUrl }, testInfo) => {
test.setTimeout(75_000);
const profile = profileForProject(testInfo.project.name);
await page.setViewportSize(profile.viewport);
const cdp = await page.context().newCDPSession(page);
await cdp.send('Network.enable');
await cdp.send('Network.clearBrowserCache');
await cdp.send('Network.emulateNetworkConditions', {
offline: false,
latency: profile.latency,
downloadThroughput: profile.download,
uploadThroughput: profile.upload,
});
await page.emulateMedia({ reducedMotion: 'no-preference' });
await page.goto('about:blank');
const loadStarted = nodePerformance.now();
await page.goto(authorizedPerformanceUrl(distUrl, profile.tier), { waitUntil: 'load', timeout: 10_000 });
const loadDuration = nodePerformance.now() - loadStarted;
expect(loadDuration).toBeLessThanOrEqual(10_000);
await expect.poll(() => page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.snapshot().lcp.length ?? 0), { timeout: 10_000 }).toBeGreaterThan(0);
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'running', { timeout: 5_000 });
await expect(page.locator('.journey-hud__quality')).toHaveText(profile.tier === 'high' ? 'High' : 'Low');
const warmupRafs = await page.evaluate(() => new Promise<number[]>((resolve) => {
const samples: number[] = [];
const frame = (now: number): void => {
samples.push(now);
if (samples.length === 121) resolve(samples);
else requestAnimationFrame(frame);
};
requestAnimationFrame(frame);
}));
const warmupIntervals = warmupRafs.slice(1).map((stamp, index) => stamp - warmupRafs[index]!);
const warmupMedian = [...warmupIntervals].sort((a, b) => a - b)[Math.floor(warmupIntervals.length / 2)]!;
const validReferenceCadence = Math.abs(warmupMedian - 16.667) <= 1;
if (process.env.PORTFOLIO_REFERENCE_LAB === '1') expect(validReferenceCadence).toBe(true);
await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.startTrace());
const nodeTraceStart = nodePerformance.now();
const at = async (offset: number): Promise<void> => {
const remaining = nodeTraceStart + offset - nodePerformance.now();
if (remaining > 0) await sleep(remaining);
};
await dispatchPointerAction(page, cdp, at, 1_000, 'start', 'button:has-text("START THE DRIVE")');
await dispatchWheelBurst(page, cdp, at, 1_500, 100);
await at(2_350);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'Tab', code: 'Tab', windowsVirtualKeyCode: 9 });
await at(2_370);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Tab', code: 'Tab', windowsVirtualKeyCode: 9 });
await expect(page.getByRole('slider', { name: 'Sound volume' })).toBeFocused();
await at(2_400);
await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.beginAction('volume', 'input[aria-label="Sound volume"]'));
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'ArrowLeft', code: 'ArrowLeft', windowsVirtualKeyCode: 37 });
await at(2_440);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'ArrowLeft', code: 'ArrowLeft', windowsVirtualKeyCode: 37 });
await expect(page.getByRole('slider', { name: 'Sound volume' })).toHaveValue('0.35');
await dispatchWheelBurst(page, cdp, at, 2_800, 100);
await dispatchPointerAction(page, cdp, at, 3_700, 'mute', 'button:has-text("Mute sound")');
await expect(page.getByRole('button', { name: 'Unmute sound' })).toBeVisible();
await dispatchWheelBurst(page, cdp, at, 4_100, 100);
await dispatchPointerAction(page, cdp, at, 5_000, 'unmute', 'button:has-text("Unmute sound")');
await dispatchWheelBurst(page, cdp, at, 5_400, 100);
await dispatchPointerAction(page, cdp, at, 6_300, 'pause', 'button:has-text("Pause motion")');
await dispatchWheelBurst(page, cdp, at, 6_700, 100);
await dispatchPointerAction(page, cdp, at, 7_600, 'resume', 'button:has-text("Resume motion")');
await dispatchWheelBurst(page, cdp, at, 8_000, 100);
await at(8_800);
await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.armNavigationSettlementBarrier());
await dispatchPointerAction(page, cdp, at, 8_900, 'toolkit', 'nav a[href="#toolkit"]');
await dispatchWheelBurst(page, cdp, at, 9_300, -100);
await dispatchWheelBurst(page, cdp, at, 10_600, -100);
await dispatchWheelBurst(page, cdp, at, 11_900, 100);
await dispatchWheelBurst(page, cdp, at, 13_200, 100);
await dispatchPointerAction(page, cdp, at, 14_500, 'contact', 'nav a[href="#contact"]');
await at(20_000);
await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.endTrace());
await expect(page).toHaveURL(/#contact$/u);
await expect(page.locator('#contact > h2')).toBeFocused();
await expect(page.locator('.journey-hud__quality')).toHaveText(profile.tier === 'high' ? 'High' : 'Low');
const snapshot = await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.snapshot());
if (snapshot === undefined || snapshot.traceStart === null || snapshot.traceEnd === null || snapshot.firstSceneFrame === null) {
throw new Error('The authenticated performance snapshot is incomplete.');
}
const expectedPeriod = warmupMedian;
const fcp = await page.evaluate(() => performance.getEntriesByName('first-contentful-paint')[0]?.startTime ?? null);
const lcp = latestLcpBeforeCutoff(snapshot.lcp, snapshot.cutoff);
const cls = calculateCls(snapshot.layoutShifts);
const tbt = calculateLabTbt(snapshot.longTasks, fcp, snapshot.traceStart);
const postRunning = postRunningLongTasks(snapshot.longTasks, snapshot.traceStart, snapshot.traceEnd);
const eventToPaint = evaluateEventToNextPaint(snapshot.actions as ScriptedActionSample[]);
const continuity = evaluateSceneContinuity(snapshot.sceneIntervals, snapshot.sceneFrames, expectedPeriod);
const lost = calculateLostPresentationShare(snapshot.pageRafs, expectedPeriod, Math.floor(20_000 / expectedPeriod) - 3);
const lateShifts = unexpectedLateShifts(snapshot.layoutShifts, snapshot.firstSceneFrame, snapshot.traceEnd);
const sceneCpuP95 = nearestRankP95(snapshot.sceneFrames.map((frame) => frame.end - frame.start));
const budgetResults = {
lcp: lcp <= 3_000,
cls: cls <= 0.1,
tbt: tbt <= 300,
postRunningLongTasks: postRunning.length === 0,
eventToNextPaint: eventToPaint.p95 <= 200,
sceneCpu: sceneCpuP95 <= profile.sceneCpuP95,
continuity: continuity.passes,
lostPresentation: lost.hasSufficientCoverage && lost.share <= profile.lostShare,
lateShifts: lateShifts.length === 0,
};
if (process.env.PORTFOLIO_REFERENCE_LAB === '1') {
expect(Object.values(budgetResults).every(Boolean)).toBe(true);
}
expect(snapshot.navigationBarrier.cancelledEpoch).not.toBeNull();
const report = {
schema: 'drive-trace.v1',
evidenceKind: 'current-chromium-developer-machine-non-reference',
gatingReferenceResult: false,
profile: profile.name,
tier: profile.tier,
browser: testInfo.project.name,
metrics: { warmupMedian, lcp, cls, tbt, postRunningLongTaskCount: postRunning.length, eventToNextPaintP95: eventToPaint.p95, sceneCpuP95, continuity, lostPresentationShare: lost.share, lateShiftCount: lateShifts.length },
budgetResults,
referenceBudgetsPass: validReferenceCadence && Object.values(budgetResults).every(Boolean),
validReferenceCadence,
pendingPhysicalReferenceRuns: 5,
};
await writeFile(testInfo.outputPath('drive-trace.v1.json'), `${JSON.stringify(report, null, 2)}\n`);
});

View File

@@ -0,0 +1,111 @@
import { describe, expect, it } from 'vitest';
import {
calculateCls,
calculateLabTbt,
calculateLostPresentationShare,
evaluateEventToNextPaint,
evaluateSceneContinuity,
latestLcpBeforeCutoff,
nearestRankP95,
postRunningLongTasks,
unexpectedLateShifts,
type ScriptedActionSample,
} from '@/testing/performance-instrumentation';
describe('performance metric math', () => {
it('uses exact nearest-rank p95 without interpolation', () => {
expect(nearestRankP95([100, 1, 3, 2, 4])).toBe(100);
expect(nearestRankP95(Array.from({ length: 20 }, (_, index) => index + 1))).toBe(19);
expect(() => nearestRankP95([])).toThrow(/sample/u);
});
it('cuts LCP off at the first trusted input or hidden transition', () => {
expect(latestLcpBeforeCutoff([100, 200, 450], 400)).toBe(200);
expect(() => latestLcpBeforeCutoff([], 400)).toThrow(/LCP/u);
});
it('calculates CLS with exact one-second and five-second session windows', () => {
expect(calculateCls([
{ startTime: 0, value: 0.01, hadRecentInput: false },
{ startTime: 1_000, value: 0.01, hadRecentInput: false },
{ startTime: 2_000, value: 0.01, hadRecentInput: false },
{ startTime: 3_000, value: 0.01, hadRecentInput: false },
{ startTime: 4_000, value: 0.01, hadRecentInput: false },
{ startTime: 5_000, value: 0.01, hadRecentInput: false },
{ startTime: 5_001, value: 0.2, hadRecentInput: false },
{ startTime: 5_100, value: 1, hadRecentInput: true },
])).toBeCloseTo(0.2);
});
it('clips lab TBT to the post-50ms portion before running', () => {
expect(calculateLabTbt([
{ startTime: 0, duration: 100 },
{ startTime: 180, duration: 100 },
{ startTime: 250, duration: 100 },
], 20, 300)).toBe(100);
expect(() => calculateLabTbt([], null, 300)).toThrow(/FCP/u);
});
it('detects every long task intersecting the post-running trace', () => {
expect(postRunningLongTasks([
{ startTime: 900, duration: 200 },
{ startTime: 20_999, duration: 2 },
{ startTime: 21_000, duration: 100 },
], 1_000, 21_000)).toEqual([
{ startTime: 900, duration: 200 },
{ startTime: 20_999, duration: 2 },
]);
});
it('requires the exact eight action confirmations and applies the conservative 16ms bound', () => {
const ids = ['start', 'volume', 'mute', 'unmute', 'pause', 'resume', 'toolkit', 'contact'] as const;
const actions: ScriptedActionSample[] = ids.map((id, index) => ({
id,
target: `target-${id}`,
dispatchTime: index * 1_000,
acknowledgementCount: 1,
mutationTime: index * 1_000 + 20,
doubleRafTime: index * 1_000 + 40,
eventTimingSupported: true,
eventEntries: index === 7 ? [{ target: `target-${id}`, startTime: index * 1_000, duration: 80, interactionId: 7 }] : [],
}));
expect(evaluateEventToNextPaint(actions)).toEqual({ values: [16, 16, 16, 16, 16, 16, 16, 80], p95: 80 });
expect(() => evaluateEventToNextPaint(actions.slice(0, 7))).toThrow(/eight/u);
expect(() => evaluateEventToNextPaint([{ ...actions[0]!, acknowledgementCount: 2 }, ...actions.slice(1)])).toThrow(/acknowledgement/u);
});
it('evaluates scene continuity at interval boundaries and callback coverage', () => {
const result = evaluateSceneContinuity(
[{ start: 0, end: 100 }],
[
{ start: 10, end: 12 },
{ start: 28, end: 30 },
{ start: 46, end: 48 },
{ start: 64, end: 66 },
{ start: 82, end: 84 },
],
16.667,
);
expect(result.passes).toBe(true);
expect(result.expectedCallbacks).toBe(5);
expect(result.actualCallbacks).toBe(5);
expect(evaluateSceneContinuity([{ start: 0, end: 100 }], [{ start: 60, end: 61 }], 16.667).passes).toBe(false);
});
it('counts lost presentation opportunities from page-level RAF intervals', () => {
const result = calculateLostPresentationShare([0, 16.667, 50.001, 66.668], 16.667, 3);
expect(result.opportunities).toBe(4);
expect(result.lost).toBe(1);
expect(result.share).toBeCloseTo(0.25);
expect(result.hasSufficientCoverage).toBe(true);
});
it('allows no unexpected late shifts', () => {
expect(unexpectedLateShifts([
{ startTime: 99, value: 1, hadRecentInput: false },
{ startTime: 101, value: 1, hadRecentInput: true },
{ startTime: 102, value: 0.1, hadRecentInput: false },
{ startTime: 201, value: 0.1, hadRecentInput: false },
], 100, 200)).toEqual([{ startTime: 102, value: 0.1, hadRecentInput: false }]);
});
});

View File

@@ -0,0 +1,167 @@
export type CycleDirection = 'forward' | 'reverse';
export interface SafeViewport { safeX: number; safeY: number; width: number; height: number }
export interface ViewportGeometry { offsetLeft: number; offsetTop: number; width: number; height: number }
export interface SafeInsets { left: number; right: number; top: number; bottom: number }
export interface TargetBox { left: number; top: number; right: number; bottom: number }
type PointerAction =
| { type: 'pointerMove'; duration: number; origin: 'viewport'; x: number; y: number }
| { type: 'pointerDown'; button: 0 }
| { type: 'pointerUp'; button: 0 }
| { type: 'pause'; duration: number };
type KeyAction =
| { type: 'keyDown'; value: string }
| { type: 'keyUp'; value: string }
| { type: 'pause'; duration: number };
export const roundHalfUp = (value: number): number => Math.floor(value + 0.5);
export const computeSafeViewport = (viewport: ViewportGeometry, insets: SafeInsets): SafeViewport => {
const width = viewport.width - insets.left - insets.right;
const height = viewport.height - insets.top - insets.bottom;
if (!(width > 0) || !(height > 0)) throw new Error('Safe viewport dimensions must be positive.');
return {
safeX: viewport.offsetLeft + insets.left,
safeY: viewport.offsetTop + insets.top,
width,
height,
};
};
export const buildSwipeCycle = (direction: CycleDirection, safe: SafeViewport) => {
const startFraction = direction === 'forward' ? 0.8 : 0.2;
const delta = direction === 'forward' ? -0.7 : 0.7;
const x = roundHalfUp(safe.safeX + 0.5 * safe.width);
const actions: PointerAction[] = [
{ type: 'pointerMove', duration: 0, origin: 'viewport', x, y: roundHalfUp(safe.safeY + safe.height * startFraction) },
{ type: 'pointerDown', button: 0 },
];
for (let index = 1; index <= 15; index += 1) {
actions.push({
type: 'pointerMove',
duration: 40,
origin: 'viewport',
x,
y: roundHalfUp(safe.safeY + safe.height * (startFraction + delta * index / 15)),
});
}
actions.push({ type: 'pointerUp', button: 0 }, { type: 'pause', duration: 400 });
return {
pointer: { type: 'pointer' as const, id: 'driveTouch' as const, parameters: { pointerType: 'touch' as const }, actions },
keyboard: { type: 'key' as const, id: 'driveKeys' as const, actions: [{ type: 'pause' as const, duration: 1_000 }] },
};
};
const driveSources = (pointerActions: PointerAction[], keyboardActions: KeyAction[]) => ({
pointer: {
type: 'pointer' as const,
id: 'driveTouch' as const,
parameters: { pointerType: 'touch' as const },
actions: pointerActions,
},
keyboard: { type: 'key' as const, id: 'driveKeys' as const, actions: keyboardActions },
});
export const buildTargetActivation = (first: TargetBox, second: TargetBox, safe: SafeViewport) => {
for (const edge of ['left', 'top', 'right', 'bottom'] as const) {
if (Math.abs(first[edge] - second[edge]) >= 1) throw new Error('Activation target box must remain stable across both samples.');
}
const left = Math.max(safe.safeX, second.left);
const top = Math.max(safe.safeY, second.top);
const right = Math.min(safe.safeX + safe.width, second.right);
const bottom = Math.min(safe.safeY + safe.height, second.bottom);
if (!(right > left) || !(bottom > top)) throw new Error('Activation target must intersect the safe viewport.');
return driveSources([
{ type: 'pointerMove', duration: 0, origin: 'viewport', x: roundHalfUp((left + right) / 2), y: roundHalfUp((top + bottom) / 2) },
{ type: 'pause', duration: 60 },
{ type: 'pointerDown', button: 0 },
{ type: 'pause', duration: 60 },
{ type: 'pointerUp', button: 0 },
], [{ type: 'pause', duration: 120 }]);
};
export const buildVolumeKeySequence = () => driveSources(
[{ type: 'pause', duration: 16_040 }],
[
{ type: 'pause', duration: 15_900 },
{ type: 'keyDown', value: '\uE004' },
{ type: 'pause', duration: 20 },
{ type: 'keyUp', value: '\uE004' },
{ type: 'pause', duration: 80 },
{ type: 'keyDown', value: '\uE012' },
{ type: 'pause', duration: 40 },
{ type: 'keyUp', value: '\uE012' },
],
);
export const chooseCycleDirection = (
direction: CycleDirection,
rawScrollY: number,
rawMaxScrollY: number,
): CycleDirection => {
const maxScrollY = Math.max(0, rawMaxScrollY);
const scrollY = Math.min(maxScrollY, Math.max(0, rawScrollY));
if (direction === 'forward' && scrollY >= maxScrollY - 2) return 'reverse';
if (direction === 'reverse' && scrollY <= 2) return 'forward';
return direction;
};
const integerRange = (start: number, endExclusive: number): number[] => Array.from(
{ length: endExclusive - start },
(_, index) => start + index,
);
export const physicalCycleOffsets = (): number[] => [
...integerRange(30, 120),
...integerRange(125, 180),
...integerRange(275, 510).filter((offset) => offset !== 389 && offset !== 390),
];
interface ProtocolEvent { offsetMs: number; action: string; assertions: string[] }
interface ProtocolBranch { offsetMs: number; action: string; assertions: string[] }
export const buildPhysicalDriveProtocol = () => {
const events: ProtocolEvent[] = [
{ offsetMs: 0, action: 'begin-idle-portrait', assertions: ['one-canvas-loop', 'geometry'] },
{ offsetMs: 15_000, action: 'activate-start-the-drive', assertions: ['state-by-15500'] },
{ offsetMs: 15_900, action: 'volume-tab-keydown', assertions: [] },
{ offsetMs: 15_920, action: 'volume-tab-keyup', assertions: ['slider-focused'] },
{ offsetMs: 16_000, action: 'volume-arrow-left-keydown', assertions: [] },
{ offsetMs: 16_040, action: 'volume-arrow-left-keyup', assertions: ['volume-35-by-16500'] },
...physicalCycleOffsets().map((offset) => ({
offsetMs: offset * 1_000,
action: 'scroll-cycle',
assertions: offset === 391
? ['sample-direction-and-safe-viewport', 'cancel-exact-pending-navigation-epoch', 'release-barrier']
: ['sample-direction-and-safe-viewport'],
})),
{ offsetMs: 120_000, action: 'rotate-landscape', assertions: ['geometry-by-125000', 'one-canvas-loop', 'tier-at-most-maximum'] },
{ offsetMs: 180_000, action: 'background', assertions: ['no-page-input-for-30s'] },
{ offsetMs: 210_000, action: 'foreground', assertions: ['lifecycle-counts-by-215000'] },
{ offsetMs: 225_000, action: 'audio-interruption', assertions: ['suspended-and-resume-sound-by-227000'] },
{ offsetMs: 230_000, action: 'activate-resume-sound', assertions: ['state-by-230500'] },
{ offsetMs: 250_000, action: 'context-loss', assertions: [] },
{ offsetMs: 252_000, action: 'context-restore', assertions: ['one-running-generation-or-static-by-257000'] },
{ offsetMs: 270_000, action: 'rotate-portrait', assertions: ['geometry-by-275000'] },
{ offsetMs: 390_000, action: 'arm-navigation-settlement-barrier', assertions: [] },
{ offsetMs: 390_000, action: 'navigate-infrastructure', assertions: ['one-navigation-epoch'] },
{ offsetMs: 510_000, action: 'background', assertions: ['no-page-input-for-30s'] },
{ offsetMs: 540_000, action: 'foreground', assertions: ['lifecycle-counts-by-545000'] },
{ offsetMs: 575_000, action: 'navigate-contact', assertions: ['state-by-575500'] },
{ offsetMs: 580_000, action: 'settle-contact', assertions: ['contact-usable', 'final-geometry'] },
{ offsetMs: 600_000, action: 'finish', assertions: ['no-crash', 'no-duplicates', 'monotonic-quality'] },
].sort((left, right) => left.offsetMs - right.offsetMs);
const branches: { live: ProtocolBranch[]; static: ProtocolBranch[] } = {
live: [
{ offsetMs: 550_000, action: 'pause-motion', assertions: ['zero-scene-raf', 'retained-frame', 'audio-independent'] },
{ offsetMs: 565_000, action: 'resume-motion', assertions: ['one-canvas-loop-by-570000'] },
],
static: [
{ offsetMs: 550_000, action: 'assert-static-without-input', assertions: ['poster', 'zero-raf', 'motion-unavailable', 'audio-independent'] },
{ offsetMs: 565_000, action: 'assert-static-without-input', assertions: ['unchanged-poster', 'zero-raf', 'controls-usable'] },
],
};
return { version: 'physical-drive.v1' as const, durationMs: 600_000, events, branches };
};

View File

@@ -0,0 +1,101 @@
import { describe, expect, it } from 'vitest';
import {
buildPhysicalDriveProtocol,
buildTargetActivation,
buildVolumeKeySequence,
buildSwipeCycle,
chooseCycleDirection,
computeSafeViewport,
physicalCycleOffsets,
roundHalfUp,
} from './physical-drive.protocol';
describe('physical-drive.v1 protocol', () => {
it('uses round-half-up safe viewport coordinates', () => {
expect(roundHalfUp(1.5)).toBe(2);
expect(computeSafeViewport({ offsetLeft: 2, offsetTop: 3, width: 390, height: 844 }, { left: 10, right: 12, top: 20, bottom: 22 })).toEqual({
safeX: 12,
safeY: 23,
width: 368,
height: 802,
});
expect(() => computeSafeViewport({ offsetLeft: 0, offsetTop: 0, width: 10, height: 10 }, { left: 5, right: 5, top: 0, bottom: 0 })).toThrow(/positive/u);
});
it('generates the literal 15-step synchronized touch cycle', () => {
const cycle = buildSwipeCycle('forward', { safeX: 0, safeY: 0, width: 100, height: 200 });
expect(cycle.pointer.id).toBe('driveTouch');
expect(cycle.keyboard.id).toBe('driveKeys');
expect(cycle.pointer.actions).toHaveLength(19);
expect(cycle.pointer.actions[0]).toEqual({ type: 'pointerMove', duration: 0, origin: 'viewport', x: 50, y: 160 });
expect(cycle.pointer.actions[2]).toEqual({ type: 'pointerMove', duration: 40, origin: 'viewport', x: 50, y: 151 });
expect(cycle.pointer.actions[16]).toEqual({ type: 'pointerMove', duration: 40, origin: 'viewport', x: 50, y: 20 });
expect(cycle.pointer.actions.at(-1)).toEqual({ type: 'pause', duration: 400 });
expect(cycle.keyboard.actions).toEqual([{ type: 'pause', duration: 1_000 }]);
});
it('switches direction only at the two-pixel boundaries', () => {
expect(chooseCycleDirection('forward', 98, 100)).toBe('reverse');
expect(chooseCycleDirection('forward', 97.99, 100)).toBe('forward');
expect(chooseCycleDirection('reverse', 2, 100)).toBe('forward');
expect(chooseCycleDirection('reverse', 2.01, 100)).toBe('reverse');
});
it('builds exact stable-box activation and synchronized volume input sources', () => {
const activation = buildTargetActivation(
{ left: 10, top: 20, right: 110, bottom: 70 },
{ left: 10.5, top: 20.5, right: 110.5, bottom: 70.5 },
{ safeX: 0, safeY: 0, width: 100, height: 100 },
);
expect(activation.pointer.actions).toEqual([
{ type: 'pointerMove', duration: 0, origin: 'viewport', x: 55, y: 46 },
{ type: 'pause', duration: 60 },
{ type: 'pointerDown', button: 0 },
{ type: 'pause', duration: 60 },
{ type: 'pointerUp', button: 0 },
]);
expect(activation.keyboard.actions).toEqual([{ type: 'pause', duration: 120 }]);
expect(() => buildTargetActivation(
{ left: 10, top: 20, right: 110, bottom: 70 },
{ left: 11, top: 20, right: 110, bottom: 70 },
{ safeX: 0, safeY: 0, width: 100, height: 100 },
)).toThrow(/stable/u);
const volume = buildVolumeKeySequence();
expect(volume.keyboard.actions).toEqual([
{ type: 'pause', duration: 15_900 },
{ type: 'keyDown', value: '\uE004' },
{ type: 'pause', duration: 20 },
{ type: 'keyUp', value: '\uE004' },
{ type: 'pause', duration: 80 },
{ type: 'keyDown', value: '\uE012' },
{ type: 'pause', duration: 40 },
{ type: 'keyUp', value: '\uE012' },
]);
expect(volume.pointer.actions).toEqual([{ type: 'pause', duration: 16_040 }]);
});
it('uses every specified cycle except the excluded 389/390-second cycles', () => {
const offsets = physicalCycleOffsets();
expect(offsets).toContain(388);
expect(offsets).not.toContain(389);
expect(offsets).not.toContain(390);
expect(offsets).toContain(391);
expect(offsets).toContain(509);
expect(offsets).not.toContain(510);
});
it('preserves normative event order and terminal-Static branches', () => {
const protocol = buildPhysicalDriveProtocol();
expect(protocol.version).toBe('physical-drive.v1');
expect(protocol.durationMs).toBe(600_000);
expect(protocol.events.filter((event) => event.offsetMs === 390_000).map((event) => event.action)).toEqual([
'arm-navigation-settlement-barrier',
'navigate-infrastructure',
]);
expect(protocol.events.find((event) => event.offsetMs === 391_000)?.assertions).toContain('cancel-exact-pending-navigation-epoch');
expect(protocol.branches.live.find((event) => event.offsetMs === 550_000)?.action).toBe('pause-motion');
expect(protocol.branches.static.find((event) => event.offsetMs === 550_000)?.action).toBe('assert-static-without-input');
expect(protocol.events.at(-1)).toMatchObject({ offsetMs: 600_000, action: 'finish' });
});
});

View File

@@ -10,7 +10,7 @@ export default defineConfig({
test: {
environment: 'happy-dom',
globals: false,
include: ['tests/unit/**/*.test.ts', 'tests/integration/**/*.test.{ts,mjs}'],
include: ['tests/unit/**/*.test.ts', 'tests/integration/**/*.test.{ts,mjs}', 'tests/performance/**/*.test.ts'],
coverage: { include: ['src/**/*.{ts,vue}'] },
},
});