name: perf description: "Use when user asks to "run perf", "performance investigation", "benchmark regression", "profiling workflow", "baseline performance". Runs structured perf investigations with baselines, profiling, hypotheses, and decisions."
/perf - Performance Investigation Workflow
Run a rigorous, evidence-driven performance investigation with strict rules, baselines, and reproducible benchmarks.
Canonical Requirements
All behavior must follow:
- •
docs/perf-requirements.md(source of truth) - •
docs/perf-research-methodology.md
Arguments
- •
--resume: Continue the latest investigation from{state-dir}/perf/investigation.json - •
--phase <phase>: Force starting phase (use only when resuming) - •
--id <id>: Set investigation id (new only) - •
--scenario <text>: Short scenario description - •
--command <cmd>: Benchmark command (prints PERF_METRICS markers) - •
--version <ver>: Baseline version label - •
--duration <seconds>: Benchmark duration override (default 60s; use smaller values for micro-benchmarks) - •
--runs <n>: Number of runs for start-to-end benchmarks (use with median aggregation) - •
--aggregate <median|mean|min|max>: Aggregation method for multi-run benchmarks (default median) - •
--quote <text>: User quote to record in logs - •
--hypotheses-file <path>: JSON file with hypothesis list (for hypotheses phase) - •
--param-env <name>: Env var for breaking-point value (default PERF_PARAM_VALUE) - •
--param-min <n>: Breaking-point min value (default 1) - •
--param-max <n>: Breaking-point max value (default 500) - •
--cpu <limit>: Constraint CPU limit (default 1) - •
--memory <limit>: Constraint memory limit (default 1GB) - •
--change <summary>: Optimization change summary - •
--verdict <continue|stop>: Decision verdict - •
--rationale <text>: Decision rationale
Phase 1: Initialize Investigation State
javascript
const { getPluginRoot } = require('@agentsys/lib/cross-platform');
const pluginRoot = getPluginRoot('perf');
if (!pluginRoot) { console.error('Error: Could not locate perf plugin root'); process.exit(1); }
const investigationState = require(`${pluginRoot}/lib/perf/investigation-state.js`);
const baselineStore = require(`${pluginRoot}/lib/perf/baseline-store.js`);
const benchmarkRunner = require(`${pluginRoot}/lib/perf/benchmark-runner.js`);
const breakingPointRunner = require(`${pluginRoot}/lib/perf/breaking-point-runner.js`);
const constraintRunner = require(`${pluginRoot}/lib/perf/constraint-runner.js`);
const profilingRunner = require(`${pluginRoot}/lib/perf/profiling-runner.js`);
const optimizationRunner = require(`${pluginRoot}/lib/perf/optimization-runner.js`);
const consolidation = require(`${pluginRoot}/lib/perf/consolidation.js`);
const checkpoint = require(`${pluginRoot}/lib/perf/checkpoint.js`);
const argumentParser = require(`${pluginRoot}/lib/perf/argument-parser.js`);
const codePaths = require(`${pluginRoot}/lib/perf/code-paths.js`);
const repoMap = require(`${pluginRoot}/lib/repo-map`);
const fs = require('fs');
const args = argumentParser.parseArguments('$ARGUMENTS');
const options = {
resume: false,
phase: null,
id: null,
scenario: '',
command: '',
version: '',
duration: null,
runs: null,
aggregate: '',
quote: '',
hypothesesFile: '',
paramEnv: 'PERF_PARAM_VALUE',
paramMin: 1,
paramMax: 500,
cpu: '1',
memory: '1GB',
change: '',
verdict: '',
rationale: ''
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--resume') options.resume = true;
else if (arg === '--phase' && args[i + 1]) options.phase = args[++i];
else if (arg === '--id' && args[i + 1]) options.id = args[++i];
else if (arg === '--scenario' && args[i + 1]) options.scenario = args[++i];
else if (arg === '--command' && args[i + 1]) options.command = args[++i];
else if (arg === '--version' && args[i + 1]) options.version = args[++i];
else if (arg === '--duration' && args[i + 1]) options.duration = Number(args[++i]);
else if (arg === '--runs' && args[i + 1]) options.runs = Number(args[++i]);
else if (arg === '--aggregate' && args[i + 1]) options.aggregate = args[++i];
else if (arg === '--quote' && args[i + 1]) options.quote = args[++i];
else if (arg === '--hypotheses-file' && args[i + 1]) options.hypothesesFile = args[++i];
else if (arg === '--param-env' && args[i + 1]) options.paramEnv = args[++i];
else if (arg === '--param-min' && args[i + 1]) options.paramMin = Number(args[++i]);
else if (arg === '--param-max' && args[i + 1]) options.paramMax = Number(args[++i]);
else if (arg === '--cpu' && args[i + 1]) options.cpu = args[++i];
else if (arg === '--memory' && args[i + 1]) options.memory = args[++i];
else if (arg === '--change' && args[i + 1]) options.change = args[++i];
else if (arg === '--verdict' && args[i + 1]) options.verdict = args[++i];
else if (arg === '--rationale' && args[i + 1]) options.rationale = args[++i];
}
const cwd = process.cwd();
const allowedPhases = investigationState.PHASES;
if (options.phase && !allowedPhases.includes(options.phase)) {
console.error(`Invalid phase: ${options.phase}. Allowed: ${allowedPhases.join(', ')}`);
process.exit(1);
}
let state = investigationState.readInvestigation(cwd);
if (options.resume) {
if (!state) {
console.error('No active investigation found. Run /perf without --resume first.');
process.exit(1);
}
} else {
state = investigationState.initializeInvestigation({
id: options.id,
phase: options.phase,
scenario: options.scenario
}, cwd);
}
if (options.phase && options.resume) {
state = investigationState.updateInvestigation({ phase: options.phase }, cwd);
}
const userQuote = options.quote || options.scenario || 'n/a';
if (options.command || options.version || options.scenario) {
state = investigationState.updateInvestigation({
scenario: {
description: options.scenario || state.scenario?.description || '',
metrics: state.scenario?.metrics || [],
successCriteria: state.scenario?.successCriteria || '',
scenarios: state.scenario?.scenarios || []
},
benchmark: {
command: options.command || state.benchmark?.command || '',
version: options.version || state.benchmark?.version || '',
duration: Number.isFinite(options.duration) ? options.duration : state.benchmark?.duration,
runs: Number.isFinite(options.runs) ? options.runs : state.benchmark?.runs,
aggregate: options.aggregate || state.benchmark?.aggregate
}
}, cwd);
}
console.log(`
## /perf Investigation
**ID**: ${state.id}
**Phase**: ${state.phase}
**Scenario**: ${state.scenario?.description || 'n/a'}
Running phase handler...
`);
const phase = state.phase;
const command = state.benchmark?.command || options.command;
const version = state.benchmark?.version || options.version;
function requireFields(fields) {
const missing = fields.filter(Boolean);
if (missing.length > 0) {
console.error(`Missing required input(s): ${missing.join(', ')}`);
process.exit(1);
}
}
const phaseRequirements = {
setup: () => requireFields([
state.scenario?.description ? '' : '--scenario',
command ? '' : '--command',
version ? '' : '--version'
]),
baseline: () => requireFields([command ? '' : '--command', version ? '' : '--version']),
'breaking-point': () => requireFields([command ? '' : '--command']),
constraints: () => requireFields([command ? '' : '--command']),
hypotheses: () => requireFields([state.scenario?.description ? '' : '--scenario']),
'code-paths': () => requireFields([state.scenario?.description ? '' : '--scenario']),
profiling: () => requireFields([]),
optimization: () => requireFields([options.change ? '' : '--change']),
decision: () => requireFields([options.verdict ? '' : '--verdict', options.rationale ? '' : '--rationale']),
consolidation: () => requireFields([version ? '' : '--version'])
};
async function runPhase() {
if (phaseRequirements[phase]) {
phaseRequirements[phase]();
}
switch (phase) {
case 'setup': {
state = investigationState.updateInvestigation({ phase: 'baseline' }, cwd);
investigationState.appendSetupLog({
id: state.id,
userQuote,
scenario: state.scenario?.description || '',
command,
version,
duration: Number.isFinite(options.duration) ? options.duration : state.benchmark?.duration,
runs: Number.isFinite(options.runs) ? options.runs : state.benchmark?.runs,
aggregate: options.aggregate || state.benchmark?.aggregate
}, cwd);
checkpoint.commitCheckpoint({
phase: 'setup',
id: state.id,
baselineVersion: version,
deltaSummary: 'n/a'
});
return;
}
case 'baseline': {
const runs = Number.isFinite(options.runs) ? options.runs : state.benchmark?.runs;
const aggregate = options.aggregate || state.benchmark?.aggregate;
const result = benchmarkRunner.runBenchmarkSeries(command, {
duration: Number.isFinite(options.duration) ? options.duration : undefined,
runs,
aggregate
});
baselineStore.writeBaseline(version, { command, metrics: result.metrics }, cwd);
const baselinePath = baselineStore.getBaselinePath(version, cwd);
investigationState.appendBaselineLog({
id: state.id,
userQuote,
command,
metrics: result.metrics,
baselinePath,
duration: Number.isFinite(options.duration) ? options.duration : state.benchmark?.duration,
runs,
aggregate: aggregate || (runs ? 'median' : undefined),
scenarios: state.scenario?.scenarios
}, cwd);
state = investigationState.updateInvestigation({ phase: 'breaking-point' }, cwd);
checkpoint.commitCheckpoint({
phase: 'baseline',
id: state.id,
baselineVersion: version,
deltaSummary: 'n/a'
});
return;
}
case 'breaking-point': {
const result = await breakingPointRunner.runBreakingPointSearch({
command,
paramEnv: options.paramEnv,
min: options.paramMin,
max: options.paramMax
});
investigationState.updateInvestigation({
breakingPoint: result.breakingPoint,
breakingPointHistory: result.history,
phase: 'constraints'
}, cwd);
investigationState.appendBreakingPointLog({
id: state.id,
userQuote,
paramEnv: options.paramEnv,
min: options.paramMin,
max: options.paramMax,
breakingPoint: result.breakingPoint,
history: result.history
}, cwd);
checkpoint.commitCheckpoint({
phase: 'breaking-point',
id: state.id,
baselineVersion: version || 'n/a',
deltaSummary: `breakingPoint=${result.breakingPoint ?? 'n/a'}`
});
return;
}
case 'constraints': {
const runs = Number.isFinite(options.runs) ? options.runs : state.benchmark?.runs;
const aggregate = options.aggregate || state.benchmark?.aggregate;
const results = constraintRunner.runConstraintTest({
command,
constraints: { cpu: options.cpu, memory: options.memory },
duration: Number.isFinite(options.duration) ? options.duration : undefined,
runs,
aggregate
});
const nextResults = Array.isArray(state.constraintResults) ? state.constraintResults : [];
nextResults.push(results);
investigationState.updateInvestigation({ constraintResults: nextResults, phase: 'hypotheses' }, cwd);
investigationState.appendConstraintLog({
id: state.id,
userQuote,
constraints: results.constraints,
delta: results.delta
}, cwd);
checkpoint.commitCheckpoint({
phase: 'constraints',
id: state.id,
baselineVersion: version || 'n/a',
deltaSummary: 'constraints'
});
return;
}
case 'hypotheses': {
const gitHistory = checkpoint.getRecentCommits(5);
let hypotheses = Array.isArray(state.hypotheses) ? state.hypotheses : [];
if (hypotheses.length === 0) {
if (!options.hypothesesFile) {
console.error('Missing hypotheses. Run perf-theory-gatherer or provide --hypotheses-file.');
process.exit(1);
}
try {
const raw = fs.readFileSync(options.hypothesesFile, 'utf8');
const parsed = JSON.parse(raw);
hypotheses = Array.isArray(parsed.hypotheses) ? parsed.hypotheses : parsed;
} catch (error) {
console.error(`Failed to load hypotheses file: ${error.message}`);
process.exit(1);
}
}
investigationState.updateInvestigation({ hypotheses, phase: 'code-paths' }, cwd);
investigationState.appendHypothesesLog({
id: state.id,
userQuote,
hypotheses,
gitHistory,
hypothesesFile: options.hypothesesFile || null
}, cwd);
checkpoint.commitCheckpoint({
phase: 'hypotheses',
id: state.id,
baselineVersion: version || 'n/a',
deltaSummary: 'n/a'
});
return;
}
case 'code-paths': {
const mapStatus = repoMap.status(cwd);
if (!mapStatus.exists) {
console.log('Repo map not found. Run /repo-map init for better code-path coverage.');
}
const repoMapStatus = mapStatus.exists
? `available (files=${mapStatus.status?.files ?? 'n/a'}, symbols=${mapStatus.status?.symbols ?? 'n/a'})`
: 'missing';
const map = repoMap.load(cwd);
const result = codePaths.collectCodePaths(map, state.scenario?.description || '');
investigationState.updateInvestigation({ codePaths: result.paths, phase: 'profiling' }, cwd);
investigationState.appendCodePathsLog({
id: state.id,
userQuote,
keywords: result.keywords,
paths: result.paths,
repoMapStatus
}, cwd);
checkpoint.commitCheckpoint({
phase: 'code-paths',
id: state.id,
baselineVersion: version || 'n/a',
deltaSummary: `paths=${result.paths.length}`
});
return;
}
case 'profiling': {
const result = profilingRunner.runProfiling({ repoPath: cwd, command });
if (!result.ok) {
console.error(`Profiling failed: ${result.error}`);
process.exit(1);
}
const nextResults = Array.isArray(state.profilingResults) ? state.profilingResults : [];
nextResults.push(result.result);
investigationState.updateInvestigation({ profilingResults: nextResults, phase: 'optimization' }, cwd);
investigationState.appendProfilingLog({
id: state.id,
userQuote,
tool: result.result.tool,
command: result.result.command,
artifacts: result.result.artifacts,
hotspots: result.result.hotspots
}, cwd);
checkpoint.commitCheckpoint({
phase: 'profiling',
id: state.id,
baselineVersion: version || 'n/a',
deltaSummary: 'n/a'
});
return;
}
case 'optimization': {
const gitHistory = checkpoint.getRecentCommits(5);
const runs = Number.isFinite(options.runs) ? options.runs : state.benchmark?.runs;
const aggregate = options.aggregate || state.benchmark?.aggregate;
const result = optimizationRunner.runOptimizationExperiment({
command,
changeSummary: options.change,
duration: Number.isFinite(options.duration) ? options.duration : undefined,
runs,
aggregate
});
const nextResults = Array.isArray(state.results) ? state.results : [];
nextResults.push(result);
investigationState.updateInvestigation({ results: nextResults, phase: 'decision' }, cwd);
investigationState.appendOptimizationLog({
id: state.id,
userQuote,
change: options.change,
delta: result.delta,
verdict: result.verdict,
gitHistory,
runs,
aggregate: aggregate || (runs ? 'median' : undefined)
}, cwd);
checkpoint.commitCheckpoint({
phase: 'optimization',
id: state.id,
baselineVersion: version || 'n/a',
deltaSummary: 'n/a'
});
return;
}
case 'decision': {
const decision = { verdict: options.verdict, rationale: options.rationale };
investigationState.updateInvestigation({ decision, phase: 'consolidation' }, cwd);
investigationState.appendDecisionLog({
id: state.id,
userQuote,
verdict: options.verdict,
rationale: options.rationale,
resultsCount: Array.isArray(state.results) ? state.results.length : 0
}, cwd);
checkpoint.commitCheckpoint({
phase: 'decision',
id: state.id,
baselineVersion: version || 'n/a',
deltaSummary: 'n/a'
});
return;
}
case 'consolidation': {
const baseline = baselineStore.readBaseline(version, cwd);
if (!baseline) {
console.error(`Baseline not found for version ${version}`);
process.exit(1);
}
const result = consolidation.consolidateBaseline({ version, baseline }, cwd);
investigationState.appendConsolidationLog({
id: state.id,
userQuote,
version,
path: result.path
}, cwd);
investigationState.updateInvestigation({ phase: 'complete' }, cwd);
checkpoint.commitCheckpoint({
phase: 'consolidation',
id: state.id,
baselineVersion: version,
deltaSummary: 'n/a'
});
return;
}
default:
return;
}
}
await runPhase();
Output
- •Updated
{state-dir}/perf/investigation.json - •Investigation log at
{state-dir}/perf/investigations/<id>.md - •Baseline files at
{state-dir}/perf/baselines/<version>.json
Begin the performance investigation now.