CodeNomad/scripts/test-render-cost-audit.mjs
Pascal André f6a18d00bd
fix: reduce compaction and cross-session responsiveness stalls
Aggregate compaction fragments before reactive SDK reduction while advancing
revision fences immediately and preserving native terminal and snapshot
authority. Keep task-tool shells keyed across payload updates and rescan bounded
membership so removals and replacements cannot leave phantom tools.

Isolate SSE callbacks and decode/dispatch diagnostics. Subscriber-local teardown
no longer destroys the healthy shared native client. Install HTTP cancellation
before preflight and fence late continuations without replaying or interrupting
already-admitted mutations. Preserve ownership and environment admission checks.

Move HTTP password derivation to async scrypt with serialized password changes
and login-authority fencing. Yield cooperatively in both structural-index phases
without changing snapshot/digest protocols; an individual SQLite step stays sync.

Add isolated red/green regressions, bounded browser/native qualification scripts,
independent reviews, source fingerprints and performance evidence for issue #824.
Synchronize the Git focus assertion with its async contract and make the opt-in
trace self-contained instead of serializing a tsx helper into the browser.

Validation includes 793 passing server tests and 6 skips after replay, UI/server/
Electron typechecks, UI/server/plugin builds, and private Windows native 2.0.21
fixtures. Renderer fixture median main-thread cost is 324.57 to 16.94 ms.
The latest full browser run remains 358 pass, 1 fail, 1 skip: its trace failure
was subsequently reproduced and corrected with a minimal Chromium check, but
the full suite has not been rerun. Store assertions use force-exit; natural
cleanup and Linux Electron to Windows LAN/HTTP with 2.0.19 remain unqualified.

Publish as a draft for review, not a universal resolution or issue-closing fix.
Refs #824.
2026-10-02 00:40:45 +02:00

57 lines
3.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { execFileSync, spawnSync } from "node:child_process"
import { createHash } from "node:crypto"
import { mkdtempSync, readFileSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { dirname, join, resolve } from "node:path"
import { fileURLToPath } from "node:url"
// Read-only source A/B in isolated loopback Chromium fixtures. No checkout,
// desktop, daemon, database or user-profile mutation is needed.
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..")
const ui = join(root, "packages/ui")
const reference = "47f9e43e3fa1a3be59c0f94932a7af0a4be20660"
const source = "packages/ui/src/components/tool-call/renderers/task.tsx"
const repetitions = Number(process.argv[2] ?? 3)
if (!Number.isInteger(repetitions) || repetitions < 1 || repetitions > 10) throw new Error("Use 1–10 repetitions")
const directory = mkdtempSync(join(tmpdir(), "codenomad-render-cost-"))
const baselinePath = join(directory, "task-baseline.tsx")
writeFileSync(baselinePath, execFileSync("git", ["show", `${reference}:${source}`], { cwd: root }))
const runs = []
for (let repetition = 0; repetition < repetitions; repetition++) {
for (const variant of ["baseline", "fixed"]) {
const env = { ...process.env, CODENOMAD_RENDER_INSTRUMENT: "off" }
if (variant === "baseline") env.CODENOMAD_RENDER_TASK_BASELINE = baselinePath
else delete env.CODENOMAD_RENDER_TASK_BASELINE
const result = spawnSync(process.execPath, ["--import", "tsx", "--test", "--test-concurrency=1",
"--test-name-pattern=^audit sample:", "tests/browser/render-cost.test.ts"], {
cwd: ui, env, encoding: "utf8", windowsHide: true, timeout: 120_000,
})
writeFileSync(join(directory, `${repetition + 1}-${variant}.log`), `${result.stdout ?? ""}\n${result.stderr ?? ""}`)
if (result.status !== 0) throw new Error(`Fixture failed (${variant}): ${result.error ?? result.stderr}; logs: ${directory}`)
const match = result.stdout.match(/render-cost-native-deltas (\{[^\r\n]*\})/)
if (!match) throw new Error(`No metrics in ${variant}; logs: ${directory}`)
const metrics = JSON.parse(match[1])
if (metrics.variant !== variant || metrics.instrumented || metrics.samples.length !== 10) throw new Error("Invalid A/B fixture metadata")
runs.push({ repetition: repetition + 1, ...metrics })
console.log(`${repetition + 1}/${repetitions} ${variant}: 10 samples recorded`)
}
}
function stats(values) {
const sorted = [...values].sort((a, b) => a - b)
const mean = values.reduce((sum, value) => sum + value, 0) / values.length
return { n: values.length, median: (sorted[(sorted.length - 1) >> 1] + sorted[sorted.length >> 1]) / 2,
min: sorted[0], max: sorted.at(-1), mean,
sampleStandardDeviation: Math.sqrt(values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / (values.length - 1)) }
}
const summary = Object.fromEntries(["baseline", "fixed"].map(variant => {
const samples = runs.filter(run => run.variant === variant).flatMap(run => run.samples)
return [variant, Object.fromEntries(["syncMs", "scriptMs", "taskMs", "layoutMs", "styleMs"].map(key => [key, stats(samples.map(sample => sample[key]))]))]
}))
const artifact = { reference, fixedSourceSha256: createHash("sha256").update(readFileSync(join(root, source))).digest("hex"),
browserPath: process.env.CODENOMAD_BROWSER_PATH ?? "Playwright default", node: process.version,
repetitions, scenario: "80 completed read steps × 8,000 characters; 3 warmup deltas, 10 single native text deltas per run",
notes: "CDP cumulative CPU deltas through two animation frames; not frame latency. Instrumentation off. Other host activity is not controlled.",
summary, runs }
writeFileSync(join(directory, "metrics.json"), JSON.stringify(artifact, null, 2))
console.log(JSON.stringify(summary, null, 2))
console.log(`Raw metrics and logs: ${join(directory, "metrics.json")}`)