Files
pbakaus_impeccable/scripts/benchmark-live-providers.mjs
T
Paul BakausandClaude 4e381305e1 Fix source-safety, detector, and lock defects in Live polling work
Addresses the review findings on #371, plus several the bots did not catch.
All fixes have regression coverage that fails on the prior code.

Source corruption:
- Vue accept dropped valueless root attrs (disabled, v-cloak) and, worse,
  rewrote @click="x" as a literal click="x" DOM attribute, because the attr
  parser was name-anchored and skipped the sigil. Tokenize the whole Vue attr
  grammar and normalize shorthands so accept round-trips directives.
- --variant was interpolated unescaped into a RegExp, so --variant '.*' matched
  the original block first and reported a successful accept while silently
  restoring the original. Validate against the digits pattern the browser and
  the /events schema already enforce.
- --id reached path.join unvalidated, so --id ../../../../etc/evil wrote and
  read receipts outside the project. Hoist the existing safeSessionId check
  into impeccable-paths and apply it at every id-to-path sink.

Accept/lock correctness:
- Plain HTML/JSX accept and discard did not catch SOURCE_LOCKED, so contention
  exited non-zero with empty stdout and the agent got no JSON to retry on.
- Lock staleness was mtime-only and never read the pid it records: a holder
  whose critical section outran 60s had its live lock swept, admitting a second
  writer to the same file, while a crashed holder blocked accepts for a full
  60s. Decide staleness by owner liveness, and release only our own lock.

Detector:
- isNeutralColor only parses computed color forms, so routing authored CSS
  through it reported inset 4px 0 0 #000 / black / #e5e7eb as chromatic
  side-tab stripes. Add an authored-color neutrality test covering hex and
  named neutrals; the fixture had no literal-color cases at all.
- Rule line numbers were off by one for every rule after the first, and
  commented-out CSS was scanned as live rules.

Server:
- An error reply carries no sourceEventType, and inferSourceEventType returned
  undefined, which acknowledgePendingEvent treats as a wildcard: a stale
  generate worker's failure consumed the user's queued Accept, which then
  reached no agent and left the browser in SAVING forever.
- The generate preflight spawned live-wrap.mjs synchronously inside the request
  handler, freezing the single-threaded server for the whole scaffold (~7.6s
  measured on this repo, 15s ceiling) and stalling Accept/Discard/SSE. Make it
  async, claiming the lease before the first await so no event double-delivers.
- Every browser checkpoint was echoed back as variant_progress, so a Tune
  slider drag remounted the preview under the user's cursor and latched the
  *_reviewable phases from the wrong trigger. Gate on the reason.

Cleanup:
- Collapse four divergent benchmark argv parsers into scripts/lib/cli-args.mjs.
  Three silently misread flags: --iterations 20 benchmarked 5, --agent llm ran
  the fake agent, --median-target=0.4 used the default threshold.
- Drop a snapshot cache this branch made write-only (it grew per session for
  the server's lifetime and was never read), a dead exported reconcile helper,
  and the unused deferReply branch.

Prepared with AI assistance under maintainer direction.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-17 13:48:44 -07:00

510 lines
20 KiB
JavaScript

#!/usr/bin/env node
import { execFile } from 'node:child_process';
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { dirname, join, resolve } from 'node:path';
import { performance } from 'node:perf_hooks';
import { promisify } from 'node:util';
import { fileURLToPath } from 'node:url';
import { bootFixtureSession, FIXTURES_DIR } from '../tests/live-e2e/session.mjs';
import { createFakeAgent } from '../tests/live-e2e/agent.mjs';
import { parseArgs, positiveIntFlag } from './lib/cli-args.mjs';
import {
clickAccept,
clickGo,
pickElement,
waitForCycling,
waitForHandshake,
} from '../tests/live-e2e/ui.mjs';
import {
BRAND_CONTRACT,
PROVIDER_PROFILES,
STRATEGIES,
applyRuntimeSourceScore,
createProviderLiveAgent,
loadBenchmarkEnv,
resolveProviderSelection,
scoreVariantOutput,
summarizeProviderRuns,
validateAcceptedCleanup,
} from './lib/live-provider-benchmark.mjs';
const execFileP = promisify(execFile);
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const FIXTURE_NAME = 'vite8-react-brand-fidelity';
const SOURCE_FILE = 'src/App.jsx';
const args = parseArgs(process.argv.slice(2));
const iterations = positiveIntFlag(args.iterations, 1);
const providers = csv(args.providers || 'anthropic,openai,google');
const strategies = csv(args.strategies || Object.keys(STRATEGIES).join(','));
const outputPath = args.output ? resolve(ROOT, String(args.output)) : null;
const loadedEnv = loadBenchmarkEnv({ repoRoot: ROOT, explicitPath: args.envFile ? resolve(String(args.envFile)) : null });
const modelOverrides = Object.fromEntries(providers.map((provider) => [provider, args[`${provider}Model`]]).filter(([, value]) => value));
const selection = resolveProviderSelection(providers, modelOverrides);
const fixture = JSON.parse(await readFile(join(FIXTURES_DIR, FIXTURE_NAME, 'fixture.json'), 'utf-8'));
const liveSpec = await readFile(join(ROOT, 'skill', 'reference', 'live.md'), 'utf-8');
validateConfiguration({ fixture, strategies, selection, liveSpec });
if (args.dryRun) {
const report = {
schemaVersion: 1,
generatedAt: new Date().toISOString(),
mode: 'dry-run',
fixture: FIXTURE_NAME,
iterations,
envFilesLoaded: loadedEnv.length,
providers: selection.map(publicProviderSelection),
strategies: strategies.map((strategy) => ({ strategy, ...STRATEGIES[strategy] })),
plannedApiCallsPerIteration: Object.fromEntries(strategies.map((strategy) => [strategy, callsPerStrategy(strategy)])),
qualityGate: qualityGateDescription(),
};
if (outputPath) await persist(report, outputPath);
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
process.exit(0);
}
const available = selection.filter((item) => item.keyPresent);
if (args.requireAll && available.length !== selection.length) {
const missing = selection.filter((item) => !item.keyPresent).map((item) => item.provider);
throw new Error(`missing API keys for: ${missing.join(', ')}`);
}
if (available.length === 0) throw new Error('no provider API keys found; use --dry-run to validate without network calls');
const needsBrowser = args.pipeline === 'e2e' || args.skipCleanupControl !== true;
const { chromium } = needsBrowser ? await import('playwright') : { chromium: null };
const browser = chromium ? await chromium.launch({ headless: args.headed !== true }) : null;
const results = [];
let cleanupControl = { passed: true, skipped: true };
try {
if (args.skipCleanupControl !== true) {
process.stderr.write('[live-provider-bench] running provider-independent Accept/cleanup control\n');
cleanupControl = await runCleanupControl({ browser, fixture });
}
if (args.cleanupOnly) {
const cleanupReport = {
schemaVersion: 1,
generatedAt: new Date().toISOString(),
mode: 'cleanup-control',
fixture: FIXTURE_NAME,
cleanupControl,
};
if (outputPath) await persist(cleanupReport, outputPath);
process.stdout.write(JSON.stringify(cleanupReport, null, 2) + '\n');
process.exitCode = cleanupControl.passed ? 0 : 1;
}
if (args.cleanupOnly) {
// Skip provider calls; the finally block still closes Chromium.
} else {
for (const providerConfig of available) {
for (const strategy of strategies) {
for (let iteration = 1; iteration <= iterations; iteration += 1) {
process.stderr.write(`[live-provider-bench] ${providerConfig.provider}/${providerConfig.model} ${strategy} run ${iteration}/${iterations}\n`);
results.push(args.pipeline === 'e2e'
? await runOne({ browser, fixture, liveSpec, providerConfig, strategy, iteration })
: await runGenerationOne({ liveSpec, providerConfig, strategy, iteration, cleanupControl }));
}
}
}
}
} finally {
if (browser) await browser.close().catch(() => {});
}
if (args.cleanupOnly) process.exit(process.exitCode || 0);
const groups = [];
for (const providerConfig of selection) {
for (const strategy of strategies) {
const runs = results.filter((run) => run.provider === providerConfig.provider && run.strategy === strategy);
if (runs.length === 0) continue;
groups.push({
provider: providerConfig.provider,
model: providerConfig.model,
strategy,
strategyConfig: STRATEGIES[strategy],
summary: summarizeProviderRuns(runs),
runs,
});
}
}
const report = {
schemaVersion: 1,
generatedAt: new Date().toISOString(),
mode: 'live',
fixture: FIXTURE_NAME,
iterations,
providers: selection.map(publicProviderSelection),
qualityGate: qualityGateDescription(),
cleanupControl,
groups,
evaluations: evaluateStrategies(groups),
totals: {
apiCalls: results.reduce((sum, run) => sum + run.providerCalls.filter((call) => call.phase !== 'parallel-assembled').length, 0),
estimatedCostUsd: roundUsd(results.reduce((sum, run) => sum + run.estimatedCostUsd, 0)),
passingRuns: results.filter((run) => run.passed).length,
totalRuns: results.length,
},
};
if (outputPath) await persist(report, outputPath);
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
async function runGenerationOne({ liveSpec: loadedLiveSpec, providerConfig, strategy, iteration, cleanupControl: cleanup }) {
const records = [];
const agent = createProviderLiveAgent({
provider: providerConfig.provider,
model: providerConfig.model,
strategy,
liveSpec: loadedLiveSpec,
onRecord: (record) => {
records.push(record);
const result = record.error ? `error=${record.error.split('\n')[0]}` : `duration=${record.durationMs ?? 0}ms`;
process.stderr.write(`[live-provider-bench:model] ${record.phase}${record.lane ? `/${record.lane}` : ''} attempt=${record.attempt} ${result}\n`);
},
});
const event = syntheticEvent(`${providerConfig.provider}-${strategy}-${iteration}`);
const startedAt = performance.now();
try {
let output;
let firstOutput;
let firstReviewableMs;
if (typeof agent.generateFirstVariant === 'function') {
firstOutput = await agent.generateFirstVariant(event, {});
firstReviewableMs = roundMs(performance.now() - startedAt);
output = await agent.generateRemainingVariants(event, { firstOutput });
} else {
output = await agent.generateVariants(event, {});
firstReviewableMs = roundMs(performance.now() - startedAt);
}
const allReadyMs = roundMs(performance.now() - startedAt);
const quality = scoreVariantOutput(output);
const estimatedCostUsd = roundUsd(records.reduce((sum, record) => sum + Number(record.estimatedCostUsd || 0), 0));
return {
provider: providerConfig.provider,
model: providerConfig.model,
strategy,
iteration,
firstReviewableMs,
allReadyMs,
acceptCleanupMs: cleanup.acceptCleanupMs ?? null,
quality,
cleanup,
firstOutputScore: firstOutput ? scoreVariantOutput(firstOutput) : quality,
providerCalls: records.map(publicProviderRecord),
estimatedCostUsd,
passed: quality.passed && cleanup.passed,
};
} catch (error) {
return {
provider: providerConfig.provider,
model: providerConfig.model,
strategy,
iteration,
error: String(error?.stack || error),
cleanup,
providerCalls: records.map(publicProviderRecord),
estimatedCostUsd: roundUsd(records.reduce((sum, record) => sum + Number(record.estimatedCostUsd || 0), 0)),
passed: false,
};
}
}
async function runCleanupControl({ browser, fixture: loadedFixture }) {
let session;
try {
session = await bootFixtureSession({
name: FIXTURE_NAME,
fixture: loadedFixture,
browser,
agent: createFakeAgent(),
wrapTarget: { classes: 'offer-card', tag: 'article', text: 'Field Notes' },
progressive: false,
log: args.verbose ? (message) => process.stderr.write(`[live-provider-bench:cleanup] ${message}\n`) : () => {},
});
await waitForHandshake(session.page);
await pickElement(session.page, loadedFixture.runtime.pickSelector);
await clickGo(session.page);
await waitForCycling(session.page, 3, { timeout: 45_000 });
const acceptAt = performance.now();
await clickAccept(session.page, { expectedVariant: 1 });
const browserClean = await waitForAcceptCleanup(session.page, session.tmp);
const acceptCleanupMs = roundMs(performance.now() - acceptAt);
const source = await readFile(join(session.tmp, SOURCE_FILE), 'utf-8');
const build = args.skipBuild ? { passed: true, skipped: true } : await verifyBuild(session.tmp);
return {
...validateAcceptedCleanup({ source, browserClean, buildPassed: build.passed }),
acceptCleanupMs,
build,
consoleErrorCount: session.consoleErrors.length,
};
} catch (error) {
return { passed: false, error: String(error?.stack || error) };
} finally {
if (session) await session.teardown();
}
}
async function runOne({ browser, fixture, liveSpec, providerConfig, strategy, iteration }) {
const records = [];
const agent = createProviderLiveAgent({
provider: providerConfig.provider,
model: providerConfig.model,
strategy,
liveSpec,
onRecord: (record) => {
records.push(record);
const result = record.error ? `error=${record.error.split('\n')[0]}` : `duration=${record.durationMs ?? 0}ms`;
process.stderr.write(`[live-provider-bench:model] ${record.phase}${record.lane ? `/${record.lane}` : ''} attempt=${record.attempt} ${result}\n`);
},
});
let session;
const startedAt = performance.now();
try {
session = await bootFixtureSession({
name: FIXTURE_NAME,
fixture,
browser,
agent,
wrapTarget: (event) => ({
classes: event.element?.classes?.join(',') || 'offer-card',
tag: event.element?.tagName?.toLowerCase() || 'article',
text: event.element?.textContent?.trim(),
}),
progressive: STRATEGIES[strategy].delivery !== 'atomic',
log: args.verbose ? (message) => process.stderr.write(`[live-provider-bench:e2e] ${message}\n`) : () => {},
});
await waitForHandshake(session.page);
await pickElement(session.page, fixture.runtime.pickSelector);
const goAt = performance.now();
const firstReady = waitForFirstReviewable(session.page);
await clickGo(session.page);
await firstReady;
const firstReviewableMs = roundMs(performance.now() - goAt);
await waitForCycling(session.page, 3, { timeout: 240_000 });
const allReadyMs = roundMs(performance.now() - goAt);
const finalRecord = [...records].reverse().find((record) => ['atomic', 'remaining', 'parallel-assembled'].includes(record.phase) && record.output);
if (!finalRecord) throw new Error('provider benchmark produced no complete variant output');
let quality = scoreVariantOutput(finalRecord.output);
const acceptAt = performance.now();
await clickAccept(session.page, { expectedVariant: 1 });
const browserClean = await waitForAcceptCleanup(session.page, session.tmp);
const acceptCleanupMs = roundMs(performance.now() - acceptAt);
const source = await readFile(join(session.tmp, SOURCE_FILE), 'utf-8');
const build = args.skipBuild ? { passed: true, skipped: true } : await verifyBuild(session.tmp);
const cleanup = validateAcceptedCleanup({ source, browserClean, buildPassed: build.passed });
quality = applyRuntimeSourceScore(quality, cleanup);
const estimatedCostUsd = roundUsd(records.reduce((sum, record) => sum + Number(record.estimatedCostUsd || 0), 0));
const passed = quality.passed && cleanup.passed;
return {
provider: providerConfig.provider,
model: providerConfig.model,
strategy,
iteration,
firstReviewableMs,
allReadyMs,
acceptCleanupMs,
endToEndMs: roundMs(performance.now() - startedAt),
quality,
cleanup,
build,
consoleErrorCount: session.consoleErrors.length,
providerCalls: records.map(publicProviderRecord),
estimatedCostUsd,
passed,
};
} catch (error) {
return {
provider: providerConfig.provider,
model: providerConfig.model,
strategy,
iteration,
error: String(error?.stack || error),
providerCalls: records.map(publicProviderRecord),
estimatedCostUsd: roundUsd(records.reduce((sum, record) => sum + Number(record.estimatedCostUsd || 0), 0)),
passed: false,
};
} finally {
if (session) await session.teardown();
}
}
async function waitForFirstReviewable(page) {
await page.waitForFunction(() => {
const query = window.__impeccableLiveQuery || ((selector) => document.querySelector(selector));
const wrapper = query('[data-impeccable-variants]');
if (!wrapper) return false;
const sourceVariants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
const debug = window.__IMPECCABLE_LIVE_CHROME_CORE__?.debugState?.();
const arrived = wrapper.dataset.impeccablePreview === 'svelte-component'
? Number(debug?.arrivedVariants || 0)
: sourceVariants.length;
return arrived >= 1;
}, undefined, { timeout: 240_000 });
}
async function waitForAcceptCleanup(page, tmp) {
const deadline = Date.now() + 45_000;
while (Date.now() < deadline) {
const browserClean = await page.evaluate(() => {
const query = window.__impeccableLiveQuery || ((selector) => document.querySelector(selector));
const wrapperGone = !query('[data-impeccable-variants]');
const state = document.documentElement.dataset.impeccableLiveState;
return wrapperGone && (!state || state === 'PICKING');
}).catch(() => false);
const source = await readFile(join(tmp, SOURCE_FILE), 'utf-8').catch(() => '');
const sourceClean = source && !/data-impeccable-|impeccable-(?:variants|carbonize|params|original)/i.test(source);
if (browserClean && sourceClean) return true;
await new Promise((resolvePromise) => setTimeout(resolvePromise, 40));
}
return false;
}
async function verifyBuild(tmp) {
const startedAt = performance.now();
try {
await execFileP('npm', ['run', 'build'], { cwd: tmp, timeout: 120_000, maxBuffer: 4 * 1024 * 1024 });
return { passed: true, durationMs: roundMs(performance.now() - startedAt) };
} catch (error) {
return {
passed: false,
durationMs: roundMs(performance.now() - startedAt),
error: String(error?.stderr || error?.message || error).slice(0, 2000),
};
}
}
function evaluateStrategies(groups) {
const evaluations = [];
for (const provider of new Set(groups.map((group) => group.provider))) {
const providerGroups = groups.filter((group) => group.provider === provider);
const baseline = providerGroups.find((group) => group.strategy === 'atomic-full');
const baselineValid = baseline?.summary.gatePassRate === 1
&& Number.isFinite(baseline?.summary.metrics.firstReviewableMs?.median);
for (const group of providerGroups) {
const summary = group.summary;
const qualityPass = summary.gatePassRate === 1 && summary.cleanupPassRate === 1;
const first = summary.metrics.firstReviewableMs?.median;
const baselineFirst = baseline?.summary.metrics.firstReviewableMs?.median;
const firstImprovement = Number.isFinite(first) && Number.isFinite(baselineFirst) && baselineFirst > 0
? Number((1 - first / baselineFirst).toFixed(4))
: null;
const latencyPass = group.strategy === 'atomic-full'
|| (baselineValid ? firstImprovement != null && firstImprovement > 0.1 : Number.isFinite(first) && first < 15_000);
evaluations.push({
provider,
model: group.model,
strategy: group.strategy,
decision: qualityPass && latencyPass ? 'accept' : 'reject',
firstReviewableImprovementVsAtomic: firstImprovement,
qualityPass,
latencyPass,
reason: !qualityPass
? 'Rejected: fidelity, source validity, or cleanup gate failed.'
: !latencyPass
? 'Rejected: first-reviewable median did not improve by more than 10%.'
: group.strategy === 'atomic-full'
? 'Control: retained as the one-call baseline.'
: !baselineValid
? 'Accepted: quality passed and first review completed under 15 seconds; the atomic control was invalid for this provider.'
: 'Accepted: materially faster first review with all quality and cleanup gates intact.',
});
}
}
return evaluations;
}
function publicProviderSelection(item) {
return {
provider: item.provider,
label: item.label,
model: item.model,
keyPresent: item.keyPresent,
pricePerMillion: item.pricePerMillion,
effort: item.effort,
priceSource: item.priceSource,
};
}
function publicProviderRecord(record) {
return {
phase: record.phase,
lane: record.lane,
attempt: record.attempt,
durationMs: record.durationMs,
totalPhaseMs: record.totalPhaseMs,
usage: record.usage,
estimatedCostUsd: record.estimatedCostUsd,
error: record.error,
outputScore: record.output ? scoreVariantOutput(record.output) : undefined,
};
}
function qualityGateDescription() {
return {
deterministic: true,
pass: 'overall >= 0.90 and every dimension >= 0.75; accepted source must build and contain no Live markers',
dimensions: ['brandFidelity', 'componentFidelity', 'tokenFidelity', 'copyFidelity', 'sourceValidity', 'acceptCleanup'],
identityLock: BRAND_CONTRACT.identity,
};
}
function syntheticEvent(id) {
const outerHTML = BRAND_CONTRACT.sourceExcerpt
.replaceAll('className=', 'class=')
.replace(/\s+/g, ' ')
.trim();
return {
id,
action: 'impeccable',
freeformPrompt: 'Make this offer easier to scan while staying unmistakably inside the existing brand and component system.',
count: 3,
mode: 'replace',
element: {
outerHTML,
tagName: 'ARTICLE',
className: 'offer-card',
classes: ['offer-card'],
textContent: BRAND_CONTRACT.requiredCopy.join(' '),
},
};
}
function callsPerStrategy(strategy) {
if (strategy === 'atomic-full') return 1;
if (strategy === 'parallel-compact') return 3;
return 2;
}
function validateConfiguration({ fixture: loadedFixture, strategies: selectedStrategies, selection: selectedProviders, liveSpec: loadedLiveSpec }) {
if (!loadedFixture.runtime?.pickSelector) throw new Error('benchmark fixture requires runtime.pickSelector');
if (!loadedLiveSpec.includes('Phase A: Extract the identity')) throw new Error('live.md identity-lock guidance not found');
for (const strategy of selectedStrategies) if (!STRATEGIES[strategy]) throw new Error(`unknown strategy ${strategy}`);
if (selectedProviders.length === 0) throw new Error('at least one provider is required');
for (const provider of selectedProviders) if (!PROVIDER_PROFILES[provider.provider]) throw new Error(`unknown provider ${provider.provider}`);
}
async function persist(report, file) {
await mkdir(dirname(file), { recursive: true });
await writeFile(file, JSON.stringify(report, null, 2) + '\n', 'utf-8');
process.stderr.write(`[live-provider-bench] wrote ${file}\n`);
}
function csv(value) {
return String(value).split(',').map((item) => item.trim()).filter(Boolean);
}
function roundMs(value) {
return Number(Number(value).toFixed(2));
}
function roundUsd(value) {
return Number(Number(value).toFixed(6));
}