Files
ozone-tech_owl_prime/scripts/replay-stability.mjs
Даня Архипов 4fcce5bff9 ops: finalize production domain and hardware benchmark tooling
Add /version.json build identity, deploy 16e7930 release markers,
DNS/Named Tunnel prep docs, portable browser benchmark, and replay
stability probe. Keep Quick Tunnel until permanent domain cutover.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 22:21:13 +00:00

155 lines
5.1 KiB
JavaScript

#!/usr/bin/env node
/**
* Replay resource stability probe — geometries/textures/programs/heap across N replays.
*
* PLAYWRIGHT_BASE_URL=http://127.0.0.1:3100 npm run perf:replay-stability
*/
import { mkdirSync, writeFileSync } from 'node:fs';
import { join, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { chromium } from 'playwright';
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const CYCLES = [0, 1, 3, 5, 10]; // cumulative replay counts to sample after
function targetUrl() {
const raw = process.env.PLAYWRIGHT_BASE_URL ?? 'http://127.0.0.1:3100';
const u = new URL(raw.includes('://') ? raw : `http://${raw}`);
u.searchParams.set('perf', '1');
u.searchParams.set('quality', 'demo');
return u.toString();
}
async function snap(page) {
return page.evaluate(() => {
const s = window.__PERF_SNAPSHOT__;
const canvasCount = document.querySelectorAll('canvas').length;
return {
at: new Date().toISOString(),
geometries: s?.geometries ?? null,
textures: s?.textures ?? null,
programs: s?.programs ?? null,
heapMb: s?.heapMb ?? null,
drawCalls: s?.drawCalls ?? null,
triangles: s?.triangles ?? null,
renderer: s?.renderer ?? null,
canvasCount,
};
});
}
async function dismissFinished(page) {
const finished = page.getByTestId('demo-finished');
if (await finished.isVisible().catch(() => false)) {
await finished.getByRole('button', { name: /Replay/i }).click();
await page.waitForTimeout(500);
}
}
async function doReplay(page) {
await dismissFinished(page);
await page.getByTestId('demo-case-0').click().catch(() => undefined);
await page.waitForTimeout(200);
const pause = page.getByTestId('demo-pause');
if (!(await pause.isVisible().catch(() => false))) {
await page.getByTestId('demo-play').click().catch(() => undefined);
}
await page.waitForTimeout(1200);
}
function classify(rows) {
const geos = rows.map((r) => r.geometries).filter((n) => typeof n === 'number');
if (geos.length < 3) return 'INCONCLUSIVE';
const first = geos[0];
const last = geos[geos.length - 1];
const mid = geos[Math.floor(geos.length / 2)];
// plateau: late samples within +2 of mid
const late = geos.slice(-2);
const plateau = late.every((g) => Math.abs(g - mid) <= 2);
const monotonic = geos.every((g, i) => i === 0 || g >= geos[i - 1]);
const growth = last - first;
if (plateau && growth <= 20) return 'LAZY_ALLOCATION_PLATEAU';
if (monotonic && growth > 20 && !plateau) return 'POSSIBLE_LEAK';
if (growth > 50) return 'CONFIRMED_LEAK';
if (growth <= 5) return 'STABLE';
return plateau ? 'LAZY_ALLOCATION_PLATEAU' : 'POSSIBLE_LEAK';
}
async function main() {
const headed = Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
const browser = await chromium.launch({
headless: !headed,
args: ['--enable-webgl', '--ignore-gpu-blocklist', '--enable-unsafe-swiftshader'],
});
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
const samples = [];
try {
await page.goto(targetUrl(), { waitUntil: 'domcontentloaded', timeout: 90_000 });
await page.waitForSelector('canvas', { timeout: 60_000 });
await page.waitForTimeout(3000);
samples.push({ cycle: 0, label: 'initial', ...(await snap(page)) });
let done = 0;
for (const target of CYCLES.slice(1)) {
while (done < target) {
await doReplay(page);
done += 1;
}
samples.push({ cycle: target, label: `after_${target}_replays`, ...(await snap(page)) });
}
// GC pause
await page.evaluate(() => {
// @ts-expect-error
if (typeof gc === 'function') gc();
}).catch(() => undefined);
await page.waitForTimeout(3000);
samples.push({ cycle: 10, label: 'after_gc_pause', ...(await snap(page)) });
} finally {
await browser.close();
}
const verdict = classify(samples);
const report = {
measuredAt: new Date().toISOString(),
url: targetUrl(),
headed,
verdict,
samples,
};
mkdirSync(join(ROOT, 'agent', 'reports'), { recursive: true });
writeFileSync(join(ROOT, 'agent', 'reports', 'replay-stability.json'), JSON.stringify(report, null, 2));
const md = `# REPLAY_RESOURCE_STABILITY
Measured: ${report.measuredAt}
Verdict: **${verdict}**
| Cycle | Heap MB | Geometries | Textures | Programs | Canvas |
| ----: | ------: | ---------: | -------: | -------: | -----: |
${samples
.map(
(s) =>
`| ${s.cycle} (${s.label}) | ${s.heapMb ?? '—'} | ${s.geometries ?? '—'} | ${s.textures ?? '—'} | ${s.programs ?? '—'} | ${s.canvasCount ?? '—'} |`,
)
.join('\n')}
## Interpretation
- **STABLE** — no meaningful growth.
- **LAZY_ALLOCATION_PLATEAU** — early growth then flat (expected for first-seen assets).
- **POSSIBLE_LEAK / CONFIRMED_LEAK** — investigate dispose paths.
Raw: \`agent/reports/replay-stability.json\`
`;
writeFileSync(join(ROOT, 'docs', 'REPLAY_RESOURCE_STABILITY.md'), md);
console.log(JSON.stringify({ ok: true, verdict, samples: samples.length }, null, 2));
}
main().catch((e) => {
console.error(e);
process.exit(1);
});