mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-10-21 05:23:19 +00:00
refactor(perf): trim the harness to what the receipts need
Drop pin-baselines.mjs: pinning is a once-per-phase paste and the script hard-coded this phase's iteration-log row, so every later phase would edit by hand anyway. Replace three hand-rolled argv parsers with node:util parseArgs. Read machine facts from node:os instead of eight subprocess probes (hw.model keeps its sysctl call). Track fixture bytes with a running counter instead of re-walking the tree per file. Delete the hover proxy that measured an argv the dock never spawns, plus unused exports (ensureDir, assertAbsolute, GENERATED_FIXTURE_DIR). Fixture output is byte-identical for the same seed (verified by full tree diff against the pre-refactor generator) and all harness metrics reproduce within run-to-run noise.
This commit is contained in:
parent
3432bac335
commit
af9c653d87
8 changed files with 55 additions and 193 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -35,9 +35,8 @@ npm-debug.log*
|
|||
.cache/
|
||||
*.sqlite
|
||||
|
||||
# Performance harness local outputs (synthetic HOME copies + run receipts)
|
||||
# Performance harness local outputs (run receipts)
|
||||
perf/results/
|
||||
perf/fixtures/generated/
|
||||
|
||||
# Build artifacts
|
||||
*.tsbuildinfo
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
"perf:fixture": "node scripts/perf/gen-fixture.mjs",
|
||||
"perf:metric": "node scripts/perf/run-metric.mjs",
|
||||
"perf:all": "node scripts/perf/run-metric.mjs --metric all",
|
||||
"perf:pin": "node scripts/perf/pin-baselines.mjs",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
|
|
|
|||
|
|
@ -27,13 +27,7 @@ These rows are wait-path measurements through isolated HOME + CLI/serve. They ar
|
|||
| refresh proxy (ms p95) | scripts/perf/gen-fixture.mjs --target-mb 30 (isolated HOME) | node scripts/perf/run-metric.mjs --metric dock-tui-proxy --home <HOME> | Mac15,14, arm64, v22.22.3, sha 93b7b9a7 | 211.4 | hover is native dock; this is payload reuse |
|
||||
| view-switch proxy (ms p95) | scripts/perf/gen-fixture.mjs --target-mb 30 (isolated HOME) | same | Mac15,14, arm64, v22.22.3, sha 93b7b9a7 | 121.3 | period week request; sidebar paint NOT VERIFIED |
|
||||
| memory RSS after cold load (bytes) | scripts/perf/gen-fixture.mjs --target-mb 30 (isolated HOME) | node scripts/perf/run-metric.mjs --metric memory --home <HOME> | Mac15,14, arm64, v22.22.3, sha 93b7b9a7 | 366460928 | 1h idle not run in this invocation. Pass --idle-ms 3600000 for the leak check. |
|
||||
| memory RSS after idle (bytes) | scripts/perf/gen-fixture.mjs --target-mb 30 (isolated HOME) | same --idle-ms 3600000 | Mac15,14, arm64, v22.22.3, sha 93b7b9a7 | 366460928 | 1h leak check only if idle_ms=3600000 |
|
||||
|
||||
## Reproduction
|
||||
|
||||
```bash
|
||||
HOME_DIR=$(mktemp -d /tmp/codeburn-perf-XXXX)
|
||||
node scripts/perf/gen-fixture.mjs --home "$HOME_DIR" --target-mb 30
|
||||
node scripts/perf/run-metric.mjs --metric all --home "$HOME_DIR"
|
||||
node scripts/perf/pin-baselines.mjs --summary perf/results/<run>/summary.json
|
||||
```
|
||||
See [perf/README.md](./README.md) — same two commands, then compare against this table.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ It extends the [release-acceptance ledger](../docs/release-acceptance/README.md)
|
|||
| Session parse | `--metric session-parse` | Cold parse of the synthetic fixture; MB/s and ms | Founder corpus (35 GB) |
|
||||
| Incremental re-parse | `--metric incremental-reparse` | Append two JSONL lines on the same inode, then re-run | Rewrite / inode change |
|
||||
| Period switch | `--metric period-switch` | Warm `serve --stdio` 7D/30D p50/p95 | Installed Desktop 250 ms p95 on the live corpus |
|
||||
| Dock/TUI interactions | `--metric dock-tui-proxy` | Badge/overview/view-switch wait path | Capacity Dock hover (native) or TTY TUI keys |
|
||||
| Dock/TUI interactions | `--metric dock-tui-proxy` | Overview-refresh and view-switch wait path | Capacity Dock hover (native, never spawns this argv) or TTY TUI keys |
|
||||
| Memory | `--metric memory` | RSS after cold load; optional `--idle-ms 3600000` | Packaged app RSS |
|
||||
|
||||
Historical receipt, not this fixture: installed Desktop 0.9.21, live corpus, 2026-08-27, 7D useful summary **18286.98 ms** after 30 s idle. Spec target: **250 ms p95** ready summary, zero raw reads on unchanged generation. Evidence: `/Users/a/Documents/Codeburn-audits/snappy-performance-20260827T095615Z/baselines/installed-0.9.21/ui-period-baseline.json`.
|
||||
|
|
@ -23,10 +23,9 @@ Historical receipt, not this fixture: installed Desktop 0.9.21, live corpus, 202
|
|||
HOME_DIR=$(mktemp -d /tmp/codeburn-perf-XXXX)
|
||||
node scripts/perf/gen-fixture.mjs --home "$HOME_DIR" --target-mb 30
|
||||
node scripts/perf/run-metric.mjs --metric all --home "$HOME_DIR"
|
||||
node scripts/perf/pin-baselines.mjs --summary perf/results/<run>/summary.json
|
||||
```
|
||||
|
||||
Or `npm run perf:all -- --home "$HOME_DIR"`.
|
||||
Or `npm run perf:all -- --home "$HOME_DIR"`. To pin a new phase, paste the run's `summary.json` numbers into `perf/BASELINES.md` by hand — pinning happens once per phase and does not need a script.
|
||||
|
||||
The fixture is synthetic (fake `/work/...` paths, lorem tool payloads). Do not copy real session files into the repo or a PR. Generated bytes live under the isolated HOME, which is gitignored.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,21 +7,23 @@
|
|||
// Layout matches provider default paths under HOME so the harness only has to
|
||||
// set HOME + CODEBURN_CACHE_DIR (same isolation as scripts/upgrade-path).
|
||||
|
||||
import { mkdirSync, writeFileSync, existsSync, rmSync, readdirSync, statSync } from 'node:fs'
|
||||
import { mkdirSync, writeFileSync, existsSync, rmSync, readdirSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { parseArgs as parseArgvOptions } from 'node:util'
|
||||
import { assertIsolatedHome, fixtureBytes } from './lib.mjs'
|
||||
|
||||
function parseArgs(argv) {
|
||||
const result = { home: '', targetMb: 30, seed: 0x9e3779b9, force: false }
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const arg = argv[i]
|
||||
if (arg === '--home') result.home = argv[++i] ?? ''
|
||||
else if (arg === '--target-mb') result.targetMb = Number(argv[++i])
|
||||
else if (arg === '--seed') result.seed = Number(argv[++i])
|
||||
else if (arg === '--force') result.force = true
|
||||
else if (arg === '--help' || arg === '-h') result.help = true
|
||||
else throw new Error('Unknown argument: ' + arg)
|
||||
}
|
||||
const { values } = parseArgvOptions({
|
||||
args: argv,
|
||||
options: {
|
||||
home: { type: 'string', default: '' },
|
||||
'target-mb': { type: 'string', default: '30' },
|
||||
seed: { type: 'string', default: String(0x9e3779b9) },
|
||||
force: { type: 'boolean', default: false },
|
||||
help: { type: 'boolean', short: 'h', default: false },
|
||||
},
|
||||
})
|
||||
const result = { home: values.home, targetMb: Number(values['target-mb']), seed: Number(values.seed), force: values.force, help: values.help }
|
||||
if (result.help) return result
|
||||
if (!result.home) throw new Error('--home is required')
|
||||
if (!Number.isFinite(result.targetMb) || result.targetMb < 1) throw new Error('--target-mb must be >= 1')
|
||||
|
|
@ -41,11 +43,9 @@ function mulberry(seed) {
|
|||
|
||||
function writeLines(path, lines) {
|
||||
mkdirSync(join(path, '..'), { recursive: true })
|
||||
writeFileSync(path, lines.join('\n') + '\n')
|
||||
}
|
||||
|
||||
function walkBytes(root) {
|
||||
return fixtureBytes(root)
|
||||
const body = lines.join('\n') + '\n'
|
||||
writeFileSync(path, body)
|
||||
return Buffer.byteLength(body)
|
||||
}
|
||||
|
||||
function main() {
|
||||
|
|
@ -103,9 +103,10 @@ function main() {
|
|||
const targetBytes = options.targetMb * 1024 * 1024
|
||||
let files = 0
|
||||
let sessions = 0
|
||||
let claudeBytes = 0
|
||||
const projectsDir = join(HOME, '.claude', 'projects')
|
||||
let i = 0
|
||||
while (walkBytes(join(HOME, '.claude')) < targetBytes * 0.88) {
|
||||
while (claudeBytes < targetBytes * 0.88) {
|
||||
const cwd = PROJECTS[i % PROJECTS.length]
|
||||
const day = (i * 3) % SPAN_DAYS
|
||||
const sid = 'perf-' + String(i).padStart(5, '0')
|
||||
|
|
@ -127,7 +128,7 @@ function main() {
|
|||
cache_creation_input_tokens: between(0, 3000),
|
||||
}, content))
|
||||
}
|
||||
writeLines(join(projectsDir, dirName, sid + '.jsonl'), lines)
|
||||
claudeBytes += writeLines(join(projectsDir, dirName, sid + '.jsonl'), lines)
|
||||
files++
|
||||
sessions++
|
||||
i++
|
||||
|
|
@ -162,7 +163,7 @@ function main() {
|
|||
sessions++
|
||||
}
|
||||
|
||||
const bytes = walkBytes(HOME)
|
||||
const bytes = fixtureBytes(HOME)
|
||||
const manifest = {
|
||||
kind: 'codeburn-perf-fixture',
|
||||
version: 1,
|
||||
|
|
|
|||
|
|
@ -5,15 +5,14 @@
|
|||
|
||||
import { spawn } from 'node:child_process'
|
||||
import { existsSync, mkdirSync, writeFileSync, statSync, readdirSync } from 'node:fs'
|
||||
import { homedir, userInfo } from 'node:os'
|
||||
import { dirname, isAbsolute, join, resolve } from 'node:path'
|
||||
import { cpus, homedir, loadavg, release, totalmem, userInfo } from 'node:os'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
|
||||
export const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..')
|
||||
export const PERF_DIR = join(REPO, 'perf')
|
||||
export const RESULTS_DIR = join(PERF_DIR, 'results')
|
||||
export const GENERATED_FIXTURE_DIR = join(PERF_DIR, 'fixtures', 'generated')
|
||||
|
||||
export const TIMINGS_HEADER = [
|
||||
'run_id', 'case_id', 'surface', 'persona', 'operation', 'trial', 'cache_state',
|
||||
|
|
@ -126,23 +125,19 @@ export async function machineSnapshot() {
|
|||
const sha = await capture('git', ['rev-parse', 'HEAD'], { cwd: REPO }).catch(() => 'unknown')
|
||||
const branch = await capture('git', ['branch', '--show-current'], { cwd: REPO }).catch(() => '')
|
||||
const dirty = await capture('git', ['status', '--porcelain=v1'], { cwd: REPO }).catch(() => '')
|
||||
const os = await capture('sw_vers', [], { cwd: REPO }).catch(() => String(process.platform))
|
||||
const arch = await capture('uname', ['-m'], { cwd: REPO }).catch(() => process.arch)
|
||||
// hw.model has no node:os equivalent; everything else does.
|
||||
const hardware = await capture('sysctl', ['-n', 'hw.model'], { cwd: REPO }).catch(() => 'unknown')
|
||||
const memoryBytes = Number(await capture('sysctl', ['-n', 'hw.memsize'], { cwd: REPO }).catch(() => '0'))
|
||||
const loadavg = await capture('sysctl', ['-n', 'vm.loadavg'], { cwd: REPO }).catch(() => '')
|
||||
const ncpu = Number(await capture('sysctl', ['-n', 'hw.ncpu'], { cwd: REPO }).catch(() => '0'))
|
||||
return {
|
||||
captured_at: new Date().toISOString(),
|
||||
sha,
|
||||
branch,
|
||||
dirty: Boolean(dirty),
|
||||
os: os.replaceAll('\n', ' | '),
|
||||
arch,
|
||||
os: process.platform + ' ' + release(),
|
||||
arch: process.arch,
|
||||
hardware,
|
||||
memory_bytes: memoryBytes,
|
||||
ncpu,
|
||||
loadavg,
|
||||
memory_bytes: totalmem(),
|
||||
ncpu: cpus().length,
|
||||
loadavg: loadavg().map(n => n.toFixed(2)).join(' '),
|
||||
node: process.version,
|
||||
cli: cliEntry().label,
|
||||
cwd: REPO,
|
||||
|
|
@ -319,16 +314,6 @@ export class ServeClient {
|
|||
}
|
||||
}
|
||||
|
||||
export function ensureDir(path) {
|
||||
mkdirSync(path, { recursive: true, mode: 0o700 })
|
||||
return path
|
||||
}
|
||||
|
||||
export function assertAbsolute(path, label) {
|
||||
if (!isAbsolute(path)) throw new Error(label + ' must be an absolute path')
|
||||
return path
|
||||
}
|
||||
|
||||
export function assertIsolatedHome(home) {
|
||||
const resolved = resolve(home)
|
||||
const homes = new Set()
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
// Pins perf/BASELINES.md from a harness summary.json. One SHA, one table.
|
||||
|
||||
import { readFileSync, writeFileSync, existsSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { PERF_DIR } from './lib.mjs'
|
||||
|
||||
function parseArgs(argv) {
|
||||
const result = { summary: '', log: true }
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const arg = argv[i]
|
||||
if (arg === '--summary') result.summary = argv[++i] ?? ''
|
||||
else if (arg === '--help' || arg === '-h') result.help = true
|
||||
else throw new Error('Unknown argument: ' + arg)
|
||||
}
|
||||
if (result.help) return result
|
||||
if (!result.summary) throw new Error('--summary is required')
|
||||
return result
|
||||
}
|
||||
|
||||
function fmt(value, digits = 1) {
|
||||
if (value == null || Number.isNaN(value)) return 'n/a'
|
||||
return Number(value).toFixed(digits)
|
||||
}
|
||||
|
||||
function metricRow(id, fixture, command, machine, number, notes) {
|
||||
return '| ' + [id, fixture, command, machine, number, notes].join(' | ') + ' |'
|
||||
}
|
||||
|
||||
function main() {
|
||||
const options = parseArgs(process.argv.slice(2))
|
||||
if (options.help) {
|
||||
console.log('Usage: node scripts/perf/pin-baselines.mjs --summary <summary.json>')
|
||||
return
|
||||
}
|
||||
const summaryPath = resolve(options.summary)
|
||||
const data = JSON.parse(readFileSync(summaryPath, 'utf8'))
|
||||
const machine = data.machine ?? {}
|
||||
const s = data.summaries ?? {}
|
||||
const machineLabel = [machine.hardware, machine.arch, machine.node, 'sha ' + String(machine.sha ?? '').slice(0, 8)].filter(Boolean).join(', ')
|
||||
const date = (machine.captured_at ?? new Date().toISOString()).slice(0, 10)
|
||||
const fixture = 'scripts/perf/gen-fixture.mjs --target-mb 30 (isolated HOME)'
|
||||
const lines = [
|
||||
'# CodeBurn performance baselines',
|
||||
'',
|
||||
'Pinned from current main before the first product change. An iteration without a before/after number from this harness does not exist.',
|
||||
'',
|
||||
'- SHA: `' + (machine.sha ?? 'unknown') + '`',
|
||||
'- Branch: `' + (machine.branch ?? '') + '`',
|
||||
'- Date: ' + date,
|
||||
'- Machine: ' + machineLabel,
|
||||
'- Node: ' + (machine.node ?? ''),
|
||||
'- CLI: ' + (machine.cli ?? ''),
|
||||
'- Summary: `' + summaryPath + '`',
|
||||
'- Historical Desktop 7D (installed 0.9.21, live corpus, 2026-08-27): 18286.98 ms useful summary. Target: 250 ms p95 ready summary. That receipt is *not* this fixture.',
|
||||
'',
|
||||
'These rows are wait-path measurements through isolated HOME + CLI/serve. They are not installed Desktop/Menu Bar UI proof.',
|
||||
'',
|
||||
'| metric | fixture | command | machine | number | notes |',
|
||||
'|---|---|---|---|---|---|',
|
||||
]
|
||||
if (s['session-parse']) {
|
||||
const d = s['session-parse']
|
||||
lines.push(metricRow('session-parse cold (ms p50/p95)', fixture, 'node scripts/perf/run-metric.mjs --metric session-parse --home <HOME>', machineLabel, fmt(d.duration_ms?.p50) + ' / ' + fmt(d.duration_ms?.p95), 'MB/s p50=' + fmt(d.mb_per_s?.p50, 3) + ' fixture_bytes=' + d.fixture_bytes))
|
||||
}
|
||||
if (s['incremental-reparse']) {
|
||||
const d = s['incremental-reparse']
|
||||
lines.push(metricRow('incremental-reparse (ms)', fixture + ' then append 2 JSONL lines', 'node scripts/perf/run-metric.mjs --metric incremental-reparse --home <HOME>', machineLabel, fmt(d.duration_ms), 'same inode append; everyday path'))
|
||||
}
|
||||
if (s['period-switch']) {
|
||||
const d = s['period-switch']
|
||||
lines.push(metricRow('period-switch first 7D after load (ms)', fixture + ' via serve --stdio', 'node scripts/perf/run-metric.mjs --metric period-switch --home <HOME>', machineLabel, fmt(d.first_week_ms), 'index-ready first 7D; analogue of 18s receipt; wait-path'))
|
||||
lines.push(metricRow('period-switch first 30D after load (ms)', fixture + ' via serve --stdio', 'same', machineLabel, fmt(d.first_30d_ms), 'index-ready first 30D; wait-path'))
|
||||
lines.push(metricRow('period-switch 7D warm (ms p50/p95)', fixture + ' via serve --stdio', 'node scripts/perf/run-metric.mjs --metric period-switch --home <HOME>', machineLabel, fmt(d.week_ms?.p50) + ' / ' + fmt(d.week_ms?.p95), 'wait-path; installed UI target remains 250ms p95'))
|
||||
lines.push(metricRow('period-switch 30D warm (ms p50/p95)', fixture + ' via serve --stdio', 'node scripts/perf/run-metric.mjs --metric period-switch --home <HOME>', machineLabel, fmt(d.days30_ms?.p50) + ' / ' + fmt(d.days30_ms?.p95), 'wait-path'))
|
||||
lines.push(metricRow('serve ready (ms)', fixture, 'same', machineLabel, fmt(d.serve_ready_ms), '{ready:true} frame'))
|
||||
}
|
||||
if (s['cold-start-cli']) {
|
||||
const d = s['cold-start-cli']
|
||||
lines.push(metricRow('cold-start desktop wait-path (ms p50/p95)', fixture, 'node scripts/perf/run-metric.mjs --metric cold-start-cli --home <HOME>', machineLabel, fmt(d.desktop_wait_ms?.p50) + ' / ' + fmt(d.desktop_wait_ms?.p95), 'status menubar-json --period today --no-timeline; UI NOT VERIFIED'))
|
||||
lines.push(metricRow('cold-start menubar wait-path (ms p50/p95)', fixture, 'same', machineLabel, fmt(d.menubar_wait_ms?.p50) + ' / ' + fmt(d.menubar_wait_ms?.p95), 'status menubar-json --provider all --period today --no-optimize; UI NOT VERIFIED'))
|
||||
}
|
||||
if (s['dock-tui-proxy']) {
|
||||
const d = s['dock-tui-proxy']
|
||||
lines.push(metricRow('refresh proxy (ms p95)', fixture, 'node scripts/perf/run-metric.mjs --metric dock-tui-proxy --home <HOME>', machineLabel, fmt(d.refresh_ms?.p95), 'hover is native dock; this is payload reuse'))
|
||||
lines.push(metricRow('view-switch proxy (ms p95)', fixture, 'same', machineLabel, fmt(d.view_switch_ms?.p95), 'period week request; sidebar paint NOT VERIFIED'))
|
||||
}
|
||||
if (s.memory) {
|
||||
const d = s.memory
|
||||
lines.push(metricRow('memory RSS after cold load (bytes)', fixture, 'node scripts/perf/run-metric.mjs --metric memory --home <HOME>', machineLabel, String(d.rss_after_cold_load_bytes ?? 'n/a'), d.note ?? ''))
|
||||
lines.push(metricRow('memory RSS after idle (bytes)', fixture, 'same --idle-ms 3600000', machineLabel, String(d.rss_after_idle_bytes ?? 'n/a'), '1h leak check only if idle_ms=3600000'))
|
||||
}
|
||||
lines.push('')
|
||||
lines.push('## Reproduction')
|
||||
lines.push('')
|
||||
lines.push('```bash')
|
||||
lines.push('HOME_DIR=$(mktemp -d /tmp/codeburn-perf-XXXX)')
|
||||
lines.push('node scripts/perf/gen-fixture.mjs --home "$HOME_DIR" --target-mb 30')
|
||||
lines.push('node scripts/perf/run-metric.mjs --metric all --home "$HOME_DIR"')
|
||||
lines.push('node scripts/perf/pin-baselines.mjs --summary perf/results/<run>/summary.json')
|
||||
lines.push('```')
|
||||
lines.push('')
|
||||
const out = join(PERF_DIR, 'BASELINES.md')
|
||||
writeFileSync(out, lines.join('\n') + '\n')
|
||||
const logPath = join(PERF_DIR, 'ITERATION-LOG.md')
|
||||
if (!existsSync(logPath)) {
|
||||
writeFileSync(logPath, '# CodeBurn performance iteration log\n\nDate | branch/PR | metric | before → after | verdict | mechanism\n---|---|---|---|---|---\n')
|
||||
}
|
||||
const logLine = '| ' + date + ' | perf/harness-phase0 | harness (all) | n/a → pinned | kept | Phase 0 harness + synthetic fixture; no product change |\n'
|
||||
const current = readFileSync(logPath, 'utf8')
|
||||
if (!current.includes('perf/harness-phase0 | harness (all)')) {
|
||||
writeFileSync(logPath, current.trimEnd() + '\n' + logLine)
|
||||
}
|
||||
console.log('wrote ' + out)
|
||||
}
|
||||
|
||||
try { main() }
|
||||
catch (error) {
|
||||
console.error(String(error?.stack ?? error))
|
||||
process.exitCode = 1
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { parseArgs as parseArgvOptions } from 'node:util'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
import {
|
||||
DESKTOP_OVERVIEW_ARGS,
|
||||
|
|
@ -38,17 +39,26 @@ const METRICS = new Set([
|
|||
])
|
||||
|
||||
function parseArgs(argv) {
|
||||
const result = { metric: '', home: '', output: '', trialsCold: 3, trialsWarm: 5, idleMs: 0 }
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const arg = argv[i]
|
||||
if (arg === '--metric') result.metric = argv[++i] ?? ''
|
||||
else if (arg === '--home') result.home = argv[++i] ?? ''
|
||||
else if (arg === '--output') result.output = argv[++i] ?? ''
|
||||
else if (arg === '--trials-cold') result.trialsCold = Number(argv[++i])
|
||||
else if (arg === '--trials-warm') result.trialsWarm = Number(argv[++i])
|
||||
else if (arg === '--idle-ms') result.idleMs = Number(argv[++i])
|
||||
else if (arg === '--help' || arg === '-h') result.help = true
|
||||
else throw new Error('Unknown argument: ' + arg)
|
||||
const { values } = parseArgvOptions({
|
||||
args: argv,
|
||||
options: {
|
||||
metric: { type: 'string', default: '' },
|
||||
home: { type: 'string', default: '' },
|
||||
output: { type: 'string', default: '' },
|
||||
'trials-cold': { type: 'string', default: '3' },
|
||||
'trials-warm': { type: 'string', default: '5' },
|
||||
'idle-ms': { type: 'string', default: '0' },
|
||||
help: { type: 'boolean', short: 'h', default: false },
|
||||
},
|
||||
})
|
||||
const result = {
|
||||
metric: values.metric,
|
||||
home: values.home,
|
||||
output: values.output,
|
||||
trialsCold: Number(values['trials-cold']),
|
||||
trialsWarm: Number(values['trials-warm']),
|
||||
idleMs: Number(values['idle-ms']),
|
||||
help: values.help,
|
||||
}
|
||||
if (result.help) return result
|
||||
if (!METRICS.has(result.metric)) throw new Error('Invalid --metric. Expected one of: ' + [...METRICS].join(', '))
|
||||
|
|
@ -255,16 +265,13 @@ async function runDockTuiProxy(ctx) {
|
|||
const client = new ServeClient(env)
|
||||
await client.start()
|
||||
await client.request(1, MENUBAR_STATUS_ARGS)
|
||||
const hover = []
|
||||
const refresh = []
|
||||
const view = []
|
||||
const rows = []
|
||||
let id = 2
|
||||
// No hover proxy: Capacity Dock hover is native quota UI and never spawns
|
||||
// this argv, so a number for it here would be theater.
|
||||
for (let trial = 1; trial <= trialsWarm; trial++) {
|
||||
const h = await client.request(id++, MENUBAR_STATUS_ARGS)
|
||||
hover.push(h.completeMs)
|
||||
rows.push(rowBase(runId, 'PERF-DOCK-HOVER-PROXY', 'serve', 'resident-warm-full', trial, 'warm', performance.now(), h.completeMs, summarizePayload(h.output),
|
||||
'PROXY ONLY: Capacity Dock hover is native quota UI, not menubar-json. This is badge-payload reuse latency.'))
|
||||
const r = await client.request(id++, DESKTOP_OVERVIEW_ARGS)
|
||||
refresh.push(r.completeMs)
|
||||
rows.push(rowBase(runId, 'PERF-REFRESH-PROXY', 'serve', 'resident-warm-full', trial, 'warm', performance.now(), r.completeMs, summarizePayload(r.output),
|
||||
|
|
@ -277,7 +284,6 @@ async function runDockTuiProxy(ctx) {
|
|||
await client.close()
|
||||
const payload = {
|
||||
metric: 'dock-tui-proxy',
|
||||
hover_proxy_ms: stats(hover),
|
||||
refresh_ms: stats(refresh),
|
||||
view_switch_ms: stats(view),
|
||||
note: 'Hover/view-switch UI remains computer-use. TUI period keys require a TTY Ink harness, not this runner.',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue